Skip to content

Commit

Permalink
feat(core): allow dev_path, dist_dir as array of paths, fixes #1897
Browse files Browse the repository at this point in the history
… (#1926)

* feat(core): allow `dev_path`, `dist_dir` as array of paths, fixes #1897

* fix: clippy
  • Loading branch information
lucasfernog committed May 31, 2021
1 parent cb6c807 commit 6ec54c5
Show file tree
Hide file tree
Showing 23 changed files with 219 additions and 409 deletions.
7 changes: 7 additions & 0 deletions .changes/dev-path-dist-dir-array.md
@@ -0,0 +1,7 @@
---
"tauri": patch
"tauri-codegen": patch
"tauri-utils": patch
---

Allow `dev_path` and `dist_dir` to be an array of root files and directories to embed.
63 changes: 33 additions & 30 deletions core/tauri-codegen/src/context.rs
Expand Up @@ -6,7 +6,7 @@ use crate::embedded_assets::{AssetOptions, EmbeddedAssets, EmbeddedAssetsError};
use proc_macro2::TokenStream;
use quote::quote;
use std::path::PathBuf;
use tauri_utils::config::{Config, WindowUrl};
use tauri_utils::config::{AppUrl, Config, WindowUrl};

/// Necessary data needed by [`context_codegen`] to generate code for a Tauri application context.
pub struct ContextData {
Expand All @@ -24,44 +24,47 @@ pub fn context_codegen(data: ContextData) -> Result<TokenStream, EmbeddedAssetsE
config_parent,
root,
} = data;

let mut options = AssetOptions::new();
if let Some(csp) = &config.tauri.security.csp {
options = options.csp(csp.clone());
}

let app_url = if dev {
&config.build.dev_path
} else {
&config.build.dist_dir
};
let assets_path = match app_url {
WindowUrl::External(_) => None,
WindowUrl::App(path) => {
if path.components().count() == 0 {
panic!(
"The `{}` configuration cannot be empty",
if dev { "devPath" } else { "distDir" }
)
}
let assets_path = config_parent.join(path);
if !assets_path.exists() {
panic!(
"The `{}` configuration is set to `{:?}` but this path doesn't exist",
if dev { "devPath" } else { "distDir" },
path
)

let assets = match app_url {
AppUrl::Url(url) => match url {
WindowUrl::External(_) => Default::default(),
WindowUrl::App(path) => {
if path.components().count() == 0 {
panic!(
"The `{}` configuration cannot be empty",
if dev { "devPath" } else { "distDir" }
)
}
let assets_path = config_parent.join(path);
if !assets_path.exists() {
panic!(
"The `{}` configuration is set to `{:?}` but this path doesn't exist",
if dev { "devPath" } else { "distDir" },
path
)
}
EmbeddedAssets::new(&assets_path, options)?
}
Some(assets_path)
}
_ => unimplemented!(),
},
AppUrl::Files(files) => EmbeddedAssets::load_paths(
files.iter().map(|p| config_parent.join(p)).collect(),
options,
)?,
_ => unimplemented!(),
};

// generate the assets inside the dist dir into a perfect hash function
let assets = if let Some(assets_path) = assets_path {
let mut options = AssetOptions::new();
if let Some(csp) = &config.tauri.security.csp {
options = options.csp(csp.clone());
}
EmbeddedAssets::new(&assets_path, options)?
} else {
Default::default()
};

// handle default window icons for Windows targets
let default_window_icon = if cfg!(windows) {
let icon_path = config
Expand Down
44 changes: 44 additions & 0 deletions core/tauri-codegen/src/embedded_assets.rs
Expand Up @@ -106,6 +106,50 @@ impl EmbeddedAssets {
.map(Self)
}

/// Compress a list of files and directories.
pub fn load_paths(
paths: Vec<PathBuf>,
options: AssetOptions,
) -> Result<Self, EmbeddedAssetsError> {
Ok(Self(
paths
.iter()
.map(|path| {
let is_file = path.is_file();
WalkDir::new(&path)
.follow_links(true)
.into_iter()
.filter_map(|entry| {
match entry {
// we only serve files, not directory listings
Ok(entry) if entry.file_type().is_dir() => None,

// compress all files encountered
Ok(entry) => Some(Self::compress_file(
if is_file {
path.parent().unwrap()
} else {
path
},
entry.path(),
&options,
)),

// pass down error through filter to fail when encountering any error
Err(error) => Some(Err(EmbeddedAssetsError::Walkdir {
path: path.to_path_buf(),
error,
})),
}
})
.collect::<Result<Vec<Asset>, _>>()
})
.flatten()
.flatten()
.collect::<_>(),
))
}

/// Use highest compression level for release, the fastest one for everything else
fn compression_level() -> i32 {
let levels = zstd::compression_level_range();
Expand Down
69 changes: 55 additions & 14 deletions core/tauri-utils/src/config.rs
Expand Up @@ -393,27 +393,40 @@ impl Default for TauriConfig {
}
}

/// The `dev_path` and `dist_dir` options.
#[derive(PartialEq, Debug, Clone, Deserialize)]
#[serde(untagged)]
#[non_exhaustive]
pub enum AppUrl {
/// A url or file path.
Url(WindowUrl),
/// An array of files.
Files(Vec<PathBuf>),
}

/// The Build configuration object.
#[derive(PartialEq, Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct BuildConfig {
/// the devPath config.
#[serde(default = "default_dev_path")]
pub dev_path: WindowUrl,
pub dev_path: AppUrl,
/// the dist config.
#[serde(default = "default_dist_path")]
pub dist_dir: WindowUrl,
pub dist_dir: AppUrl,
/// Whether we should inject the Tauri API on `window.__TAURI__` or not.
#[serde(default)]
pub with_global_tauri: bool,
}

fn default_dev_path() -> WindowUrl {
WindowUrl::External(Url::parse("http://localhost:8080").unwrap())
fn default_dev_path() -> AppUrl {
AppUrl::Url(WindowUrl::External(
Url::parse("http://localhost:8080").unwrap(),
))
}

fn default_dist_path() -> WindowUrl {
WindowUrl::App("../dist".into())
fn default_dist_path() -> AppUrl {
AppUrl::Url(WindowUrl::App("../dist".into()))
}

impl Default for BuildConfig {
Expand Down Expand Up @@ -465,7 +478,7 @@ pub struct PluginConfig(pub HashMap<String, JsonValue>);
/// application using tauri while only parsing it once (in the build script).
#[cfg(feature = "build")]
mod build {
use std::convert::identity;
use std::{convert::identity, path::Path};

use proc_macro2::TokenStream;
use quote::{quote, ToTokens, TokenStreamExt};
Expand Down Expand Up @@ -511,6 +524,15 @@ mod build {
quote! { vec![#(#items),*] }
}

/// Create a `PathBuf` constructor `TokenStream`.
///
/// e.g. `"Hello World" -> String::from("Hello World").
/// This takes a `&String` to reduce casting all the `&String` -> `&str` manually.
fn path_buf_lit(s: impl AsRef<Path>) -> TokenStream {
let s = s.as_ref().to_string_lossy().into_owned();
quote! { ::std::path::PathBuf::from(#s) }
}

/// Create a map constructor, mapping keys and values with other `TokenStream`s.
///
/// This function is pretty generic because the types of keys AND values get transformed.
Expand Down Expand Up @@ -612,8 +634,8 @@ mod build {

tokens.append_all(match self {
Self::App(path) => {
let path = path.to_string_lossy().to_string();
quote! { #prefix::App(::std::path::PathBuf::from(#path)) }
let path = path_buf_lit(&path);
quote! { #prefix::App(#path) }
}
Self::External(url) => {
let url = url.as_str();
Expand Down Expand Up @@ -779,6 +801,22 @@ mod build {
}
}

impl ToTokens for AppUrl {
fn to_tokens(&self, tokens: &mut TokenStream) {
let prefix = quote! { ::tauri::api::config::AppUrl };

tokens.append_all(match self {
Self::Url(url) => {
quote! { #prefix::Url(#url) }
}
Self::Files(files) => {
let files = vec_lit(files, path_buf_lit);
quote! { #prefix::Files(#files) }
}
})
}
}

impl ToTokens for BuildConfig {
fn to_tokens(&self, tokens: &mut TokenStream) {
let dev_path = &self.dev_path;
Expand Down Expand Up @@ -810,8 +848,7 @@ mod build {

impl ToTokens for SystemTrayConfig {
fn to_tokens(&self, tokens: &mut TokenStream) {
let icon_path = self.icon_path.to_string_lossy().to_string();
let icon_path = quote! { ::std::path::PathBuf::from(#icon_path) };
let icon_path = path_buf_lit(&self.icon_path);
literal_struct!(tokens, SystemTrayConfig, icon_path);
}
}
Expand Down Expand Up @@ -936,8 +973,10 @@ mod test {

// create a build config
let build = BuildConfig {
dev_path: WindowUrl::External(Url::parse("http://localhost:8080").unwrap()),
dist_dir: WindowUrl::App("../dist".into()),
dev_path: AppUrl::Url(WindowUrl::External(
Url::parse("http://localhost:8080").unwrap(),
)),
dist_dir: AppUrl::Url(WindowUrl::App("../dist".into())),
with_global_tauri: false,
};

Expand All @@ -948,7 +987,9 @@ mod test {
assert_eq!(d_updater, tauri.updater);
assert_eq!(
d_path,
WindowUrl::External(Url::parse("http://localhost:8080").unwrap())
AppUrl::Url(WindowUrl::External(
Url::parse("http://localhost:8080").unwrap()
))
);
assert_eq!(d_title, tauri.windows[0].title);
assert_eq!(d_windows, tauri.windows);
Expand Down
6 changes: 3 additions & 3 deletions core/tauri/src/manager.rs
Expand Up @@ -8,7 +8,7 @@
use crate::{
api::{
assets::Assets,
config::{Config, WindowUrl},
config::{AppUrl, Config, WindowUrl},
path::{resolve_path, BaseDirectory},
PackageInfo,
},
Expand Down Expand Up @@ -282,15 +282,15 @@ impl<P: Params> WindowManager<P> {
#[cfg(dev)]
fn get_url(&self) -> String {
match &self.inner.config.build.dev_path {
WindowUrl::External(url) => url.to_string(),
AppUrl::Url(WindowUrl::External(url)) => url.to_string(),
_ => "tauri://localhost".into(),
}
}

#[cfg(custom_protocol)]
fn get_url(&self) -> String {
match &self.inner.config.build.dist_dir {
WindowUrl::External(url) => url.to_string(),
AppUrl::Url(WindowUrl::External(url)) => url.to_string(),
_ => "tauri://localhost".into(),
}
}
Expand Down
1 change: 0 additions & 1 deletion core/tauri/test/fixture/src-tauri/tauri.conf.json
Expand Up @@ -3,7 +3,6 @@
"distDir": "../dist",
"devPath": "http://localhost:4000"
},
"ctx": {},
"tauri": {
"bundle": {
"identifier": "studio.tauri.example",
Expand Down
File renamed without changes.
4 changes: 2 additions & 2 deletions examples/commands/src-tauri/tauri.conf.json
@@ -1,7 +1,7 @@
{
"build": {
"distDir": "../public",
"devPath": "../public",
"distDir": ["../index.html"],
"devPath": ["../index.html"],
"beforeDevCommand": "",
"beforeBuildCommand": ""
},
Expand Down
File renamed without changes.
4 changes: 2 additions & 2 deletions examples/helloworld/src-tauri/tauri.conf.json
@@ -1,7 +1,7 @@
{
"build": {
"distDir": "../public",
"devPath": "../public",
"distDir": ["../index.html"],
"devPath": ["../index.html"],
"beforeDevCommand": "",
"beforeBuildCommand": ""
},
Expand Down
File renamed without changes.
6 changes: 3 additions & 3 deletions examples/multiwindow/src-tauri/tauri.conf.json
@@ -1,7 +1,7 @@
{
"build": {
"distDir": "../dist",
"devPath": "../dist",
"distDir": ["../index.html"],
"devPath": ["../index.html"],
"withGlobalTauri": true
},
"tauri": {
Expand Down Expand Up @@ -45,4 +45,4 @@
"active": false
}
}
}
}
File renamed without changes.
4 changes: 2 additions & 2 deletions examples/params/src-tauri/tauri.conf.json
@@ -1,7 +1,7 @@
{
"build": {
"distDir": "../public",
"devPath": "../public",
"distDir": ["../index.html"],
"devPath": ["../index.html"],
"beforeDevCommand": "",
"beforeBuildCommand": ""
},
Expand Down
File renamed without changes.
4 changes: 2 additions & 2 deletions examples/state/src-tauri/tauri.conf.json
@@ -1,7 +1,7 @@
{
"build": {
"distDir": "../public",
"devPath": "../public",
"distDir": ["../index.html"],
"devPath": ["../index.html"],
"beforeDevCommand": "",
"beforeBuildCommand": ""
},
Expand Down
File renamed without changes.

0 comments on commit 6ec54c5

Please sign in to comment.