mirror of
https://github.com/tauri-apps/tauri.git
synced 2024-08-16 19:30:27 +03:00
feat(core): allow defining global API script on plugin build (#9156)
* feat(core): allow defining global API script on plugin build Adds `tauri_plugin::Builder::global_api_script_path` so plugin authors can define the JavaScript global API bindings (supposed to be injected to `window.__TAURI__`) at compile time, so the string is only part of the binary when withGlobalTauri is true. Currently this needs to be done manually at runtime (and it's always added to the binary via include_str). * prefix variable * use list of scripts instead of combining them * static str * header [skip ci] * slice
This commit is contained in:
parent
3f039c18b1
commit
e227fe02f9
8
.changes/global-api-script-path-plugins.md
Normal file
8
.changes/global-api-script-path-plugins.md
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
---
|
||||||
|
"tauri": patch:feat
|
||||||
|
"tauri-codegen": patch:feat
|
||||||
|
"tauri-build": patch:feat
|
||||||
|
"tauri-plugin": patch:feat
|
||||||
|
---
|
||||||
|
|
||||||
|
Allow plugins to define (at compile time) JavaScript that are initialized when `withGlobalTauri` is true.
|
5
.changes/plugin-global-api-script.md
Normal file
5
.changes/plugin-global-api-script.md
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"tauri-plugin": patch:feat
|
||||||
|
---
|
||||||
|
|
||||||
|
Added `Builder::global_api_script_path` to define a JavaScript file containing the initialization script for the plugin API bindings when `withGlobalTauri` is used.
|
5
.changes/tauri-utils-plugin-module.md
Normal file
5
.changes/tauri-utils-plugin-module.md
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"tauri-utils": patch:feat
|
||||||
|
---
|
||||||
|
|
||||||
|
Added the `plugin` module.
|
@ -524,6 +524,8 @@ pub fn try_build(attributes: Attributes) -> Result<()> {
|
|||||||
|
|
||||||
acl::save_acl_manifests(&acl_manifests)?;
|
acl::save_acl_manifests(&acl_manifests)?;
|
||||||
|
|
||||||
|
tauri_utils::plugin::load_global_api_scripts(&out_dir);
|
||||||
|
|
||||||
println!("cargo:rustc-env=TAURI_ENV_TARGET_TRIPLE={target_triple}");
|
println!("cargo:rustc-env=TAURI_ENV_TARGET_TRIPLE={target_triple}");
|
||||||
|
|
||||||
// TODO: far from ideal, but there's no other way to get the target dir, see <https://github.com/rust-lang/cargo/issues/5457>
|
// TODO: far from ideal, but there's no other way to get the target dir, see <https://github.com/rust-lang/cargo/issues/5457>
|
||||||
|
@ -22,6 +22,7 @@ use tauri_utils::html::{
|
|||||||
inject_nonce_token, parse as parse_html, serialize_node as serialize_html_node, NodeRef,
|
inject_nonce_token, parse as parse_html, serialize_node as serialize_html_node, NodeRef,
|
||||||
};
|
};
|
||||||
use tauri_utils::platform::Target;
|
use tauri_utils::platform::Target;
|
||||||
|
use tauri_utils::plugin::GLOBAL_API_SCRIPT_FILE_LIST_PATH;
|
||||||
use tauri_utils::tokens::{map_lit, str_lit};
|
use tauri_utils::tokens::{map_lit, str_lit};
|
||||||
|
|
||||||
use crate::embedded_assets::{AssetOptions, CspHashes, EmbeddedAssets, EmbeddedAssetsError};
|
use crate::embedded_assets::{AssetOptions, CspHashes, EmbeddedAssets, EmbeddedAssetsError};
|
||||||
@ -456,6 +457,36 @@ pub fn context_codegen(data: ContextData) -> Result<TokenStream, EmbeddedAssetsE
|
|||||||
let resolved = Resolved::resolve(&acl, capabilities, target).expect("failed to resolve ACL");
|
let resolved = Resolved::resolve(&acl, capabilities, target).expect("failed to resolve ACL");
|
||||||
let runtime_authority = quote!(#root::ipc::RuntimeAuthority::new(#acl_tokens, #resolved));
|
let runtime_authority = quote!(#root::ipc::RuntimeAuthority::new(#acl_tokens, #resolved));
|
||||||
|
|
||||||
|
let plugin_global_api_script_file_list_path = out_dir.join(GLOBAL_API_SCRIPT_FILE_LIST_PATH);
|
||||||
|
let plugin_global_api_script =
|
||||||
|
if config.app.with_global_tauri && plugin_global_api_script_file_list_path.exists() {
|
||||||
|
let file_list_str = std::fs::read_to_string(plugin_global_api_script_file_list_path)
|
||||||
|
.expect("failed to read plugin global API script paths");
|
||||||
|
let file_list = serde_json::from_str::<Vec<PathBuf>>(&file_list_str)
|
||||||
|
.expect("failed to parse plugin global API script paths");
|
||||||
|
|
||||||
|
let mut plugins = Vec::new();
|
||||||
|
for path in file_list {
|
||||||
|
plugins.push(std::fs::read_to_string(&path).unwrap_or_else(|e| {
|
||||||
|
panic!(
|
||||||
|
"failed to read plugin global API script {}: {e}",
|
||||||
|
path.display()
|
||||||
|
)
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(plugins)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
let plugin_global_api_script = if let Some(scripts) = plugin_global_api_script {
|
||||||
|
let scripts = scripts.into_iter().map(|s| quote!(#s));
|
||||||
|
quote!(::std::option::Option::Some(&[#(#scripts),*]))
|
||||||
|
} else {
|
||||||
|
quote!(::std::option::Option::None)
|
||||||
|
};
|
||||||
|
|
||||||
Ok(quote!({
|
Ok(quote!({
|
||||||
#[allow(unused_mut, clippy::let_and_return)]
|
#[allow(unused_mut, clippy::let_and_return)]
|
||||||
let mut context = #root::Context::new(
|
let mut context = #root::Context::new(
|
||||||
@ -466,7 +497,8 @@ pub fn context_codegen(data: ContextData) -> Result<TokenStream, EmbeddedAssetsE
|
|||||||
#package_info,
|
#package_info,
|
||||||
#info_plist,
|
#info_plist,
|
||||||
#pattern,
|
#pattern,
|
||||||
#runtime_authority
|
#runtime_authority,
|
||||||
|
#plugin_global_api_script
|
||||||
);
|
);
|
||||||
#with_tray_icon_code
|
#with_tray_icon_code
|
||||||
context
|
context
|
||||||
|
@ -31,6 +31,7 @@ pub fn plugin_config<T: DeserializeOwned>(name: &str) -> Option<T> {
|
|||||||
pub struct Builder<'a> {
|
pub struct Builder<'a> {
|
||||||
commands: &'a [&'static str],
|
commands: &'a [&'static str],
|
||||||
global_scope_schema: Option<schemars::schema::RootSchema>,
|
global_scope_schema: Option<schemars::schema::RootSchema>,
|
||||||
|
global_api_script_path: Option<PathBuf>,
|
||||||
android_path: Option<PathBuf>,
|
android_path: Option<PathBuf>,
|
||||||
ios_path: Option<PathBuf>,
|
ios_path: Option<PathBuf>,
|
||||||
}
|
}
|
||||||
@ -40,6 +41,7 @@ impl<'a> Builder<'a> {
|
|||||||
Self {
|
Self {
|
||||||
commands,
|
commands,
|
||||||
global_scope_schema: None,
|
global_scope_schema: None,
|
||||||
|
global_api_script_path: None,
|
||||||
android_path: None,
|
android_path: None,
|
||||||
ios_path: None,
|
ios_path: None,
|
||||||
}
|
}
|
||||||
@ -51,6 +53,14 @@ impl<'a> Builder<'a> {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets the path to the script that is injected in the webview when the `withGlobalTauri` configuration is set to true.
|
||||||
|
///
|
||||||
|
/// This is usually an IIFE that injects the plugin API JavaScript bindings to `window.__TAURI__`.
|
||||||
|
pub fn global_api_script_path<P: Into<PathBuf>>(mut self, path: P) -> Self {
|
||||||
|
self.global_api_script_path.replace(path.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Sets the Android project path.
|
/// Sets the Android project path.
|
||||||
pub fn android_path<P: Into<PathBuf>>(mut self, android_path: P) -> Self {
|
pub fn android_path<P: Into<PathBuf>>(mut self, android_path: P) -> Self {
|
||||||
self.android_path.replace(android_path.into());
|
self.android_path.replace(android_path.into());
|
||||||
@ -118,6 +128,10 @@ impl<'a> Builder<'a> {
|
|||||||
acl::build::define_global_scope_schema(global_scope_schema, &name, &out_dir)?;
|
acl::build::define_global_scope_schema(global_scope_schema, &name, &out_dir)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(path) = self.global_api_script_path {
|
||||||
|
tauri_utils::plugin::define_global_api_script_path(path);
|
||||||
|
}
|
||||||
|
|
||||||
mobile::setup(self.android_path, self.ios_path)?;
|
mobile::setup(self.android_path, self.ios_path)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -29,6 +29,7 @@ pub mod html;
|
|||||||
pub mod io;
|
pub mod io;
|
||||||
pub mod mime_type;
|
pub mod mime_type;
|
||||||
pub mod platform;
|
pub mod platform;
|
||||||
|
pub mod plugin;
|
||||||
/// Prepare application resources and sidecars.
|
/// Prepare application resources and sidecars.
|
||||||
#[cfg(feature = "resources")]
|
#[cfg(feature = "resources")]
|
||||||
pub mod resources;
|
pub mod resources;
|
||||||
|
51
core/tauri-utils/src/plugin.rs
Normal file
51
core/tauri-utils/src/plugin.rs
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
// Copyright 2019-2024 Tauri Programme within The Commons Conservancy
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
//! Compile-time and runtime types for Tauri plugins.
|
||||||
|
#[cfg(feature = "build")]
|
||||||
|
pub use build::*;
|
||||||
|
|
||||||
|
#[cfg(feature = "build")]
|
||||||
|
mod build {
|
||||||
|
use std::{
|
||||||
|
env::vars_os,
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
};
|
||||||
|
|
||||||
|
const GLOBAL_API_SCRIPT_PATH_KEY: &str = "GLOBAL_API_SCRIPT_PATH";
|
||||||
|
/// Known file name of the file that contains an array with the path of all API scripts defined with [`define_global_api_script_path`].
|
||||||
|
pub const GLOBAL_API_SCRIPT_FILE_LIST_PATH: &str = "__global-api-script.js";
|
||||||
|
|
||||||
|
/// Defines the path to the global API script using Cargo instructions.
|
||||||
|
pub fn define_global_api_script_path(path: PathBuf) {
|
||||||
|
println!(
|
||||||
|
"cargo:{GLOBAL_API_SCRIPT_PATH_KEY}={}",
|
||||||
|
path
|
||||||
|
.canonicalize()
|
||||||
|
.expect("failed to canonicalize global API script path")
|
||||||
|
.display()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Collects the path of all the global API scripts defined with [`define_global_api_script_path`]
|
||||||
|
/// and saves them to the out dir with filename [`GLOBAL_API_SCRIPT_FILE_LIST_PATH`].
|
||||||
|
pub fn load_global_api_scripts(out_dir: &Path) {
|
||||||
|
let mut scripts = Vec::new();
|
||||||
|
|
||||||
|
for (key, value) in vars_os() {
|
||||||
|
let key = key.to_string_lossy();
|
||||||
|
|
||||||
|
if key.starts_with("DEP_") && key.ends_with(GLOBAL_API_SCRIPT_PATH_KEY) {
|
||||||
|
let script_path = PathBuf::from(value);
|
||||||
|
scripts.push(script_path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::fs::write(
|
||||||
|
out_dir.join(GLOBAL_API_SCRIPT_FILE_LIST_PATH),
|
||||||
|
serde_json::to_string(&scripts).expect("failed to serialize global API script paths"),
|
||||||
|
)
|
||||||
|
.expect("failed to write global API script");
|
||||||
|
}
|
||||||
|
}
|
@ -388,6 +388,7 @@ pub struct Context<R: Runtime> {
|
|||||||
pub(crate) _info_plist: (),
|
pub(crate) _info_plist: (),
|
||||||
pub(crate) pattern: Pattern,
|
pub(crate) pattern: Pattern,
|
||||||
pub(crate) runtime_authority: RuntimeAuthority,
|
pub(crate) runtime_authority: RuntimeAuthority,
|
||||||
|
pub(crate) plugin_global_api_scripts: Option<&'static [&'static str]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R: Runtime> fmt::Debug for Context<R> {
|
impl<R: Runtime> fmt::Debug for Context<R> {
|
||||||
@ -397,7 +398,8 @@ impl<R: Runtime> fmt::Debug for Context<R> {
|
|||||||
.field("default_window_icon", &self.default_window_icon)
|
.field("default_window_icon", &self.default_window_icon)
|
||||||
.field("app_icon", &self.app_icon)
|
.field("app_icon", &self.app_icon)
|
||||||
.field("package_info", &self.package_info)
|
.field("package_info", &self.package_info)
|
||||||
.field("pattern", &self.pattern);
|
.field("pattern", &self.pattern)
|
||||||
|
.field("plugin_global_api_scripts", &self.plugin_global_api_scripts);
|
||||||
|
|
||||||
#[cfg(all(desktop, feature = "tray-icon"))]
|
#[cfg(all(desktop, feature = "tray-icon"))]
|
||||||
d.field("tray_icon", &self.tray_icon);
|
d.field("tray_icon", &self.tray_icon);
|
||||||
@ -500,6 +502,7 @@ impl<R: Runtime> Context<R> {
|
|||||||
info_plist: (),
|
info_plist: (),
|
||||||
pattern: Pattern,
|
pattern: Pattern,
|
||||||
runtime_authority: RuntimeAuthority,
|
runtime_authority: RuntimeAuthority,
|
||||||
|
plugin_global_api_scripts: Option<&'static [&'static str]>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
config,
|
config,
|
||||||
@ -512,6 +515,7 @@ impl<R: Runtime> Context<R> {
|
|||||||
_info_plist: info_plist,
|
_info_plist: info_plist,
|
||||||
pattern,
|
pattern,
|
||||||
runtime_authority,
|
runtime_authority,
|
||||||
|
plugin_global_api_scripts,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -188,6 +188,9 @@ pub struct AppManager<R: Runtime> {
|
|||||||
/// Application pattern.
|
/// Application pattern.
|
||||||
pub pattern: Arc<Pattern>,
|
pub pattern: Arc<Pattern>,
|
||||||
|
|
||||||
|
/// Global API scripts collected from plugins.
|
||||||
|
pub plugin_global_api_scripts: Arc<Option<&'static [&'static str]>>,
|
||||||
|
|
||||||
/// Application Resources Table
|
/// Application Resources Table
|
||||||
pub(crate) resources_table: Arc<Mutex<ResourceTable>>,
|
pub(crate) resources_table: Arc<Mutex<ResourceTable>>,
|
||||||
}
|
}
|
||||||
@ -274,6 +277,7 @@ impl<R: Runtime> AppManager<R> {
|
|||||||
app_icon: context.app_icon,
|
app_icon: context.app_icon,
|
||||||
package_info: context.package_info,
|
package_info: context.package_info,
|
||||||
pattern: Arc::new(context.pattern),
|
pattern: Arc::new(context.pattern),
|
||||||
|
plugin_global_api_scripts: Arc::new(context.plugin_global_api_scripts),
|
||||||
resources_table: Arc::default(),
|
resources_table: Arc::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -208,6 +208,12 @@ impl<R: Runtime> WebviewManager<R> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(plugin_global_api_scripts) = &*app_manager.plugin_global_api_scripts {
|
||||||
|
for script in plugin_global_api_scripts.iter() {
|
||||||
|
webview_attributes = webview_attributes.initialization_script(script);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pending.webview_attributes = webview_attributes;
|
pending.webview_attributes = webview_attributes;
|
||||||
|
|
||||||
let mut registered_scheme_protocols = Vec::new();
|
let mut registered_scheme_protocols = Vec::new();
|
||||||
|
@ -127,6 +127,7 @@ pub fn mock_context<R: Runtime, A: Assets<R>>(assets: A) -> crate::Context<R> {
|
|||||||
_info_plist: (),
|
_info_plist: (),
|
||||||
pattern: Pattern::Brownfield,
|
pattern: Pattern::Brownfield,
|
||||||
runtime_authority: RuntimeAuthority::new(Default::default(), Resolved::default()),
|
runtime_authority: RuntimeAuthority::new(Default::default(), Resolved::default()),
|
||||||
|
plugin_global_api_scripts: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
16
examples/api/src-tauri/Cargo.lock
generated
16
examples/api/src-tauri/Cargo.lock
generated
@ -3152,7 +3152,7 @@ checksum = "e1fc403891a21bcfb7c37834ba66a547a8f402146eba7265b5a6d88059c9ff2f"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri"
|
name = "tauri"
|
||||||
version = "2.0.0-beta.10"
|
version = "2.0.0-beta.11"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bytes",
|
"bytes",
|
||||||
@ -3201,7 +3201,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri-build"
|
name = "tauri-build"
|
||||||
version = "2.0.0-beta.8"
|
version = "2.0.0-beta.9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"cargo_toml",
|
"cargo_toml",
|
||||||
@ -3223,7 +3223,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri-codegen"
|
name = "tauri-codegen"
|
||||||
version = "2.0.0-beta.8"
|
version = "2.0.0-beta.9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64 0.22.0",
|
"base64 0.22.0",
|
||||||
"brotli",
|
"brotli",
|
||||||
@ -3248,7 +3248,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri-macros"
|
name = "tauri-macros"
|
||||||
version = "2.0.0-beta.8"
|
version = "2.0.0-beta.9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"heck",
|
"heck",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
@ -3260,7 +3260,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri-plugin"
|
name = "tauri-plugin"
|
||||||
version = "2.0.0-beta.8"
|
version = "2.0.0-beta.9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"glob",
|
"glob",
|
||||||
@ -3286,7 +3286,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri-runtime"
|
name = "tauri-runtime"
|
||||||
version = "2.0.0-beta.8"
|
version = "2.0.0-beta.9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"gtk",
|
"gtk",
|
||||||
"http",
|
"http",
|
||||||
@ -3302,7 +3302,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri-runtime-wry"
|
name = "tauri-runtime-wry"
|
||||||
version = "2.0.0-beta.8"
|
version = "2.0.0-beta.9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cocoa",
|
"cocoa",
|
||||||
"gtk",
|
"gtk",
|
||||||
@ -3324,7 +3324,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri-utils"
|
name = "tauri-utils"
|
||||||
version = "2.0.0-beta.8"
|
version = "2.0.0-beta.9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aes-gcm",
|
"aes-gcm",
|
||||||
"brotli",
|
"brotli",
|
||||||
|
7
examples/api/src-tauri/tauri-plugin-sample/api-iife.js
Normal file
7
examples/api/src-tauri/tauri-plugin-sample/api-iife.js
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
// Copyright 2019-2024 Tauri Programme within The Commons Conservancy
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
if ('__TAURI__' in window) {
|
||||||
|
window.__TAURI__.sample = {}
|
||||||
|
}
|
@ -8,5 +8,6 @@ fn main() {
|
|||||||
tauri_plugin::Builder::new(COMMANDS)
|
tauri_plugin::Builder::new(COMMANDS)
|
||||||
.android_path("android")
|
.android_path("android")
|
||||||
.ios_path("ios")
|
.ios_path("ios")
|
||||||
|
.global_api_script_path("./api-iife.js")
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user