Skip to content

Commit edb11c1

Browse files
authored
feat(build): support plugins that are defined in app crate (#8781)
* feat(build): support plugins that are defined in app crate * dx
1 parent 052e8b4 commit edb11c1

10 files changed

Lines changed: 170 additions & 53 deletions

File tree

.changes/inline-plugins.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'tauri-build': patch:enhance
3+
---
4+
5+
Added `Attributes::plugin()` to register a plugin that is inlined in the application crate.

core/tauri-build/src/lib.rs

Lines changed: 90 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ use tauri_utils::{
2424
};
2525

2626
use std::{
27+
collections::HashMap,
2728
env::var_os,
2829
fs::copy,
2930
path::{Path, PathBuf},
@@ -331,6 +332,41 @@ impl WindowsAttributes {
331332
}
332333
}
333334

335+
/// Definition of a plugin that is part of the Tauri application instead of having its own crate.
336+
///
337+
/// By default it generates a plugin manifest that parses permissions from the `permissions/$plugin-name` directory.
338+
/// To change the glob pattern that is used to find permissions, use [`Self::permissions_path_pattern`].
339+
///
340+
/// To autogenerate permissions for each of the plugin commands, see [`Self::commands`].
341+
#[derive(Debug, Default)]
342+
pub struct InlinedPlugin {
343+
commands: &'static [&'static str],
344+
permissions_path_pattern: Option<&'static str>,
345+
}
346+
347+
impl InlinedPlugin {
348+
pub fn new() -> Self {
349+
Self::default()
350+
}
351+
352+
/// Define a list of commands that gets permissions autogenerated in the format of `allow-$command` and `deny-$command`
353+
/// where $command is the command in kebab-case.
354+
pub fn commands(mut self, commands: &'static [&'static str]) -> Self {
355+
self.commands = commands;
356+
self
357+
}
358+
359+
/// Sets a glob pattern that is used to find the permissions of this inlined plugin.
360+
///
361+
/// **Note:** You must emit [rerun-if-changed] instructions for the plugin permissions directory.
362+
///
363+
/// By default it is `./permissions/$plugin-name/**/*`
364+
pub fn permissions_path_pattern(mut self, pattern: &'static str) -> Self {
365+
self.permissions_path_pattern.replace(pattern);
366+
self
367+
}
368+
}
369+
334370
/// The attributes used on the build.
335371
#[derive(Debug, Default)]
336372
pub struct Attributes {
@@ -339,6 +375,7 @@ pub struct Attributes {
339375
capabilities_path_pattern: Option<&'static str>,
340376
#[cfg(feature = "codegen")]
341377
codegen: Option<codegen::context::CodegenContext>,
378+
inlined_plugins: HashMap<&'static str, InlinedPlugin>,
342379
}
343380

344381
impl Attributes {
@@ -365,6 +402,14 @@ impl Attributes {
365402
self
366403
}
367404

405+
/// Adds the given plugin to the list of inlined plugins (a plugin that is part of your application).
406+
///
407+
/// See [`InlinedPlugin`] for more information.
408+
pub fn plugin(mut self, name: &'static str, plugin: InlinedPlugin) -> Self {
409+
self.inlined_plugins.insert(name, plugin);
410+
self
411+
}
412+
368413
#[cfg(feature = "codegen")]
369414
#[cfg_attr(docsrs, doc(cfg(feature = "codegen")))]
370415
#[must_use]
@@ -473,7 +518,51 @@ pub fn try_build(attributes: Attributes) -> Result<()> {
473518
let out_dir = PathBuf::from(std::env::var("OUT_DIR").unwrap());
474519

475520
manifest::check(&config, &mut manifest)?;
476-
let plugin_manifests = acl::get_plugin_manifests()?;
521+
let mut plugin_manifests = acl::get_plugin_manifests()?;
522+
for (name, plugin) in attributes.inlined_plugins {
523+
let plugin_out_dir = out_dir.join("plugins").join(name);
524+
525+
let mut permission_files = if plugin.commands.is_empty() {
526+
Vec::new()
527+
} else {
528+
tauri_utils::acl::build::autogenerate_command_permissions(
529+
&plugin_out_dir,
530+
plugin.commands,
531+
"",
532+
);
533+
tauri_utils::acl::build::define_permissions(
534+
&plugin_out_dir.join("*").to_string_lossy(),
535+
name,
536+
&plugin_out_dir,
537+
)?
538+
};
539+
540+
if let Some(pattern) = plugin.permissions_path_pattern {
541+
permission_files.extend(tauri_utils::acl::build::define_permissions(
542+
pattern,
543+
name,
544+
&plugin_out_dir,
545+
)?);
546+
} else {
547+
let default_permissions_path = Path::new("permissions").join(name);
548+
println!(
549+
"cargo:rerun-if-changed={}",
550+
default_permissions_path.display()
551+
);
552+
permission_files.extend(tauri_utils::acl::build::define_permissions(
553+
&default_permissions_path
554+
.join("**")
555+
.join("*")
556+
.to_string_lossy(),
557+
name,
558+
&plugin_out_dir,
559+
)?);
560+
}
561+
562+
let manifest = tauri_utils::acl::plugin::Manifest::new(permission_files, None);
563+
plugin_manifests.insert(name.into(), manifest);
564+
}
565+
477566
std::fs::write(
478567
out_dir.join(PLUGIN_MANIFESTS_FILE_NAME),
479568
serde_json::to_string(&plugin_manifests)?,

examples/api/src-tauri/build.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ fn main() {
88
codegen = codegen.dev();
99
}
1010

11-
tauri_build::try_build(tauri_build::Attributes::new().codegen(codegen))
12-
.expect("failed to run tauri-build");
11+
tauri_build::try_build(tauri_build::Attributes::new().codegen(codegen).plugin(
12+
"app-menu",
13+
tauri_build::InlinedPlugin::new().commands(&["toggle", "popup"]),
14+
))
15+
.expect("failed to run tauri-build");
1316
}

examples/api/src-tauri/capabilities/run-app.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
"description": "permissions to run the app",
55
"windows": ["main", "main-*"],
66
"permissions": [
7+
"app-menu:default",
78
"sample:allow-ping-scoped",
89
"sample:global-scope",
910
"path:default",
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[default]
2+
description = "Default permissions for the plugin"
3+
permissions = ["allow-toggle", "allow-popup"]

examples/api/src-tauri/src/cmd.rs

Lines changed: 0 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -29,37 +29,3 @@ pub fn perform_request(endpoint: String, body: RequestBody) -> ApiResponse {
2929
message: "message response".into(),
3030
}
3131
}
32-
33-
#[cfg(all(desktop, not(target_os = "macos")))]
34-
#[command]
35-
pub fn toggle_menu<R: tauri::Runtime>(window: tauri::Window<R>) {
36-
if window.is_menu_visible().unwrap_or_default() {
37-
let _ = window.hide_menu();
38-
} else {
39-
let _ = window.show_menu();
40-
}
41-
}
42-
43-
#[cfg(target_os = "macos")]
44-
#[command]
45-
pub fn toggle_menu<R: tauri::Runtime>(
46-
app: tauri::AppHandle<R>,
47-
app_menu: tauri::State<'_, crate::AppMenu<R>>,
48-
) {
49-
if let Some(menu) = app.remove_menu().unwrap() {
50-
app_menu.0.lock().unwrap().replace(menu);
51-
} else {
52-
app
53-
.set_menu(app_menu.0.lock().unwrap().clone().expect("no app menu"))
54-
.unwrap();
55-
}
56-
}
57-
58-
#[cfg(desktop)]
59-
#[command]
60-
pub fn popup_context_menu<R: tauri::Runtime>(
61-
window: tauri::Window<R>,
62-
popup_menu: tauri::State<'_, crate::PopupMenu<R>>,
63-
) {
64-
window.popup_menu(&popup_menu.0).unwrap();
65-
}

examples/api/src-tauri/src/lib.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
mod cmd;
66
#[cfg(desktop)]
7+
mod menu_plugin;
8+
#[cfg(desktop)]
79
mod tray;
810

911
use serde::Serialize;
@@ -46,6 +48,7 @@ pub fn run_app<R: Runtime, F: FnOnce(&App<R>) + Send + 'static>(
4648
let handle = app.handle();
4749
tray::create_tray(handle)?;
4850
handle.plugin(tauri_plugin_cli::init())?;
51+
handle.plugin(menu_plugin::init())?;
4952
}
5053

5154
#[cfg(target_os = "macos")]
@@ -140,10 +143,6 @@ pub fn run_app<R: Runtime, F: FnOnce(&App<R>) + Send + 'static>(
140143
.invoke_handler(tauri::generate_handler![
141144
cmd::log_operation,
142145
cmd::perform_request,
143-
#[cfg(desktop)]
144-
cmd::toggle_menu,
145-
#[cfg(desktop)]
146-
cmd::popup_context_menu
147146
])
148147
.build(tauri::tauri_build_context!())
149148
.expect("error while building tauri application");
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
2+
// SPDX-License-Identifier: Apache-2.0
3+
// SPDX-License-Identifier: MIT
4+
5+
use tauri::{
6+
command,
7+
plugin::{Builder, TauriPlugin},
8+
Runtime,
9+
};
10+
11+
#[cfg(not(target_os = "macos"))]
12+
#[command]
13+
pub fn toggle<R: tauri::Runtime>(window: tauri::Window<R>) {
14+
if window.is_menu_visible().unwrap_or_default() {
15+
let _ = window.hide_menu();
16+
} else {
17+
let _ = window.show_menu();
18+
}
19+
}
20+
21+
#[cfg(target_os = "macos")]
22+
#[command]
23+
pub fn toggle<R: tauri::Runtime>(
24+
app: tauri::AppHandle<R>,
25+
app_menu: tauri::State<'_, crate::AppMenu<R>>,
26+
) {
27+
if let Some(menu) = app.remove_menu().unwrap() {
28+
app_menu.0.lock().unwrap().replace(menu);
29+
} else {
30+
app
31+
.set_menu(app_menu.0.lock().unwrap().clone().expect("no app menu"))
32+
.unwrap();
33+
}
34+
}
35+
36+
#[command]
37+
pub fn popup<R: tauri::Runtime>(
38+
window: tauri::Window<R>,
39+
popup_menu: tauri::State<'_, crate::PopupMenu<R>>,
40+
) {
41+
window.popup_menu(&popup_menu.0).unwrap();
42+
}
43+
44+
pub fn init<R: Runtime>() -> TauriPlugin<R> {
45+
Builder::new("app-menu")
46+
.invoke_handler(tauri::generate_handler![popup, toggle])
47+
.build()
48+
}

examples/api/src/App.svelte

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<script>
2-
import { onMount, tick } from "svelte";
2+
import { onMount, tick } from 'svelte'
33
import { writable } from 'svelte/store'
44
import { invoke } from '@tauri-apps/api/core'
55
@@ -13,7 +13,7 @@
1313
1414
document.addEventListener('keydown', (event) => {
1515
if (event.ctrlKey && event.key === 'b') {
16-
invoke('toggle_menu')
16+
invoke('plugin:app-menu|toggle')
1717
}
1818
})
1919
@@ -81,7 +81,7 @@
8181
8282
// Console
8383
let messages = writable([])
84-
let consoleTextEl;
84+
let consoleTextEl
8585
async function onMessage(value) {
8686
messages.update((r) => [
8787
...r,
@@ -90,10 +90,10 @@
9090
`<pre><strong class="text-accent dark:text-darkAccent">[${new Date().toLocaleTimeString()}]:</strong> ` +
9191
(typeof value === 'string' ? value : JSON.stringify(value, null, 1)) +
9292
'</pre>'
93-
},
93+
}
9494
])
95-
await tick();
96-
if (consoleTextEl) consoleTextEl.scrollTop = consoleTextEl.scrollHeight;
95+
await tick()
96+
if (consoleTextEl) consoleTextEl.scrollTop = consoleTextEl.scrollHeight
9797
}
9898
9999
// this function is renders HTML without sanitizing it so it's insecure
@@ -106,10 +106,10 @@
106106
`<pre><strong class="text-accent dark:text-darkAccent">[${new Date().toLocaleTimeString()}]:</strong> ` +
107107
html +
108108
'</pre>'
109-
},
109+
}
110110
])
111-
await tick();
112-
if (consoleTextEl) consoleTextEl.scrollTop = consoleTextEl.scrollHeight;
111+
await tick()
112+
if (consoleTextEl) consoleTextEl.scrollTop = consoleTextEl.scrollHeight
113113
}
114114
115115
function clear() {
@@ -329,13 +329,16 @@
329329
hover:bg-hoverOverlay dark:hover:bg-darkHoverOverlay
330330
active:bg-hoverOverlay/25 dark:active:bg-darkHoverOverlay/25
331331
"
332-
on:keypress={(e) => e.key === "Enter"? clear() : {} }
332+
on:keypress={(e) => (e.key === 'Enter' ? clear() : {})}
333333
on:click={clear}
334334
>
335335
<div class="i-codicon-clear-all" />
336336
</div>
337337
</div>
338-
<div bind:this={consoleTextEl} class="px-2 overflow-y-auto all:font-mono code-block all:text-xs select-text mr-2">
338+
<div
339+
bind:this={consoleTextEl}
340+
class="px-2 overflow-y-auto all:font-mono code-block all:text-xs select-text mr-2"
341+
>
339342
{#each $messages as r}
340343
{@html r.html}
341344
{/each}

examples/api/src/views/Welcome.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
})
1818
1919
function contextMenu() {
20-
invoke('popup_context_menu')
20+
invoke('plugin:app-menu|popup')
2121
}
2222
</script>
2323

0 commit comments

Comments
 (0)