Skip to content

Commit

Permalink
Major refactor of pipeline system to support next wave of features.
Browse files Browse the repository at this point in the history
All assets which are to be processed by trunk must now be declared as
HTML `link` elements as such:
`<link data-trunk rel="rust|sass|..." data-attr0 data-attr1/>`.
The links may appear anywhere in the HTML and Trunk will process them and
replace them or delete them based on the associated pipeline's output.
If the link element does not have the `data-trunk` attribute, it will not
be processed.

The `--manifest-path` flag has been removed in favor of the new link
style: `<link rel="rust" href="path/to/Cargo.toml"/>`. This is optional,
and will default to the Cargo.toml of the source HTML's parent dir if
not specified.

closes #50
  • Loading branch information
thedodd committed Oct 6, 2020
1 parent 212e901 commit 7701795
Show file tree
Hide file tree
Showing 22 changed files with 1,016 additions and 668 deletions.
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,16 @@ changelog
This changelog follows the patterns described here: https://keepachangelog.com/en/1.0.0/.

## Unreleased
- Made a lot of refactoring progress to be able to support [#28](https://github.com/thedodd/trunk/issues/28) & [#46](https://github.com/thedodd/trunk/issues/46). More work remains to be done, but the foundation to be able to knock these items out is now in place.

### changed
- All assets which are to be processed by trunk must now be declared as HTML `link` elements as such: `<link data-trunk rel="rust|sass|css|icon|copy-file|..." data-attr0 data-attr1/>`. The links may appear anywhere in the HTML and Trunk will process them and replace them or delete them based on the associated pipeline's output. If the link element does not have the `data-trunk` attribute, it will not be processed.

### fixed
- Fixed [#50](https://github.com/thedodd/trunk/issues/50): the ability to copy a file or an entire dir into the dist dir is now supported with two different pipeline types: `<link data-trunk rel="copy-file" href="target/file"/>` and `<link data-trunk rel="copy-dir" href="target/dir"/>` respectively.

### removed
- The `manifest-path` option has been removed from all Trunk subcommands. The path to the `Cargo.toml` is now specified in the source HTML as `<link rel="rust" href="path/to/Cargo.toml"/>`. The `href="..."` attribute may be omitted, in which case Trunk will look for a `Cargo.toml` within the same directory as the source HTML. If the `href` attribute points to a directory, Trunk will look for the `Cargo.toml` file in that directory.

## 0.6.0
### added
Expand Down
268 changes: 140 additions & 128 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "trunk"
version = "0.6.0"
version = "0.7.0"
edition = "2018"
description = "Build, bundle & ship your Rust WASM application to the web."
license = "MIT/Apache-2.0"
Expand Down
1 change: 0 additions & 1 deletion examples/yew/Trunk.toml
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
[build]
target = "index.html"
dist = "dist"
manifest = "Cargo.toml"
10 changes: 6 additions & 4 deletions examples/yew/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>Trunk | Yew | YBC</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.0/css/bulma.min.css"/>
<link rel="stylesheet" href="src/index.scss"/>
<link rel="stylesheet" href="src/app.css"/>
<link rel="icon" href="src/yew.svg"/>
<link rel="trunk-dist" href="src/yew.svg"/>

<link data-trunk rel="scss" href="src/index.scss"/>
<link data-trunk rel="css" href="src/app.css"/>
<link data-trunk rel="icon" href="src/yew.svg"/>
<link data-trunk rel="copy-file" href="src/yew.svg"/>
<base data-trunk-public-url/>
</head>
<body>
<link data-trunk rel="rust"/>
</body>
</html>
31 changes: 6 additions & 25 deletions src/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,7 @@ use indicatif::ProgressBar;

use crate::common::{BUILDING, ERROR, SUCCESS};
use crate::config::RtcBuild;
use crate::pipelines::cargo::CargoBuild;
use crate::pipelines::html::HtmlPipeline;
use crate::pipelines::wasmbg::WasmBindgen;
use crate::pipelines::HtmlPipeline;

/// A system used for building a Rust WASM app & bundling its assets.
///
Expand All @@ -21,11 +19,7 @@ use crate::pipelines::wasmbg::WasmBindgen;
pub struct BuildSystem {
/// Runtime config.
cfg: Arc<RtcBuild>,
/// Cargo build pipeline system.
cargo_build_pipeline: Arc<CargoBuild>,
/// WASM bindgen build pipeline system.
wasm_bindgen_pipeline: Arc<WasmBindgen>,
/// HTML build pipeline system.
/// HTML build pipeline.
html_pipeline: Arc<HtmlPipeline>,
/// The build system progress bar for displaying the state of the build system overall.
progress: ProgressBar,
Expand All @@ -37,17 +31,9 @@ impl BuildSystem {
/// Reducing the number of assumptions here should help us to stay flexible when adding new
/// commands, rafctoring and the like.
pub async fn new(cfg: Arc<RtcBuild>, progress: ProgressBar) -> Result<Self> {
let mode_segment = if cfg.release { "release" } else { "debug" };
let bindgen_out = Arc::new(cfg.manifest.metadata.target_directory.join("wasm-bindgen").join(mode_segment));

let cargo_build_pipeline = Arc::new(CargoBuild::new(cfg.clone(), progress.clone()));
let wasm_bindgen_pipeline = Arc::new(WasmBindgen::new(cfg.clone(), bindgen_out, progress.clone()));
let html_pipeline = Arc::new(HtmlPipeline::new(cfg.clone(), progress.clone())?);

Ok(Self {
cfg,
cargo_build_pipeline,
wasm_bindgen_pipeline,
html_pipeline,
progress,
})
Expand Down Expand Up @@ -78,19 +64,14 @@ impl BuildSystem {
}

async fn do_build(&mut self) -> Result<()> {
// Spawn cargo build. It will run concurrently without polling.
let cargo_handle = self.cargo_build_pipeline.clone().spawn();
// TODO: delete the contents of the `dist/.current` dir (currently in flight elsewhere).

// Ensure the output dist directory is in place.
fs::create_dir_all(self.cfg.dist.as_path()).await?;

// Spawn the wasm-bindgen call, it will await the cargo build.
let wasmbg_handle = self.wasm_bindgen_pipeline.clone().spawn(cargo_handle);

// Spawn the source HTML pipeline. This will spawn all other asset pipelines derived from
// the source HTML, and will await the cargo build & wasm-bindgen build in order to
// generate the final HTML.
self.html_pipeline.clone().spawn(wasmbg_handle).await?;
// Spawn the source HTML pipeline. This will spawn all other pipelines derived from
// the source HTML, and will ultimately generate and write the final HTML.
self.html_pipeline.clone().spawn().await?;
Ok(())
}
}
4 changes: 2 additions & 2 deletions src/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

use std::path::PathBuf;

use anyhow::{Context, Result};
use anyhow::{anyhow, Context, Result};
use async_std::path::PathBuf as AsyncPathBuf;
use async_std::task::spawn_blocking;

Expand All @@ -24,7 +24,7 @@ pub fn parse_public_url(val: &str) -> String {
/// A utility function to recursively copy a directory.
pub async fn copy_dir_recursive(from_dir: PathBuf, to_dir: PathBuf) -> Result<()> {
if !AsyncPathBuf::from(&from_dir).exists().await {
return Ok(());
return Err(anyhow!("directory can not be copied as it does not exist {:?}", &from_dir));
}
spawn_blocking(move || {
let opts = fs_extra::dir::CopyOptions {
Expand Down
13 changes: 4 additions & 9 deletions src/config/manifest.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::path::PathBuf;
use std::path::Path;

use anyhow::{anyhow, Result};
use async_std::task::spawn_blocking;
Expand All @@ -16,16 +16,11 @@ pub struct CargoMetadata {
}

impl CargoMetadata {
/// Get the project's cargo metadata of the CWD, or of the project specified by the given manifest path.
pub async fn new(manifest: &Option<PathBuf>) -> Result<Self> {
// Fetch the cargo project's metadata.
// Create a new instance from the Cargo.toml at the given path.
pub async fn new(manifest: &Path) -> Result<Self> {
let mut cmd = MetadataCommand::new();
if let Some(manifest) = manifest.as_ref() {
cmd.manifest_path(manifest);
}
cmd.manifest_path(manifest);
let metadata = spawn_blocking(move || cmd.exec()).await?;

// Get a handle to this project's package info.
let package = metadata
.root_package()
.cloned()
Expand Down
21 changes: 4 additions & 17 deletions src/config/models.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use serde::Deserialize;
use structopt::StructOpt;

use crate::common::parse_public_url;
use crate::config::{CargoMetadata, RtcBuild, RtcClean, RtcServe, RtcWatch};
use crate::config::{RtcBuild, RtcClean, RtcServe, RtcWatch};

/// Config options for the build system.
#[derive(Clone, Debug, Default, Deserialize, StructOpt)]
Expand All @@ -25,9 +25,6 @@ pub struct ConfigOptsBuild {
/// The public URL from which assets are to be served [default: /]
#[structopt(long, parse(from_str=parse_public_url))]
pub public_url: Option<String>,
/// Path to Cargo.toml [default: Cargo.toml]
#[structopt(long = "manifest-path", parse(from_os_str))]
pub manifest: Option<PathBuf>,
}

/// Config options for the watch system.
Expand Down Expand Up @@ -104,8 +101,7 @@ impl ConfigOpts {
let base_layer = Self::file_and_env_layers(config)?;
let build_layer = Self::cli_opts_layer_build(cli_build, base_layer);
let build_opts = build_layer.build.unwrap_or_default();
let manifest = CargoMetadata::new(&build_opts.manifest).await?;
Ok(Arc::new(RtcBuild::new(manifest, build_opts)?))
Ok(Arc::new(RtcBuild::new(build_opts)?))
}

/// Extract the runtime config for the watch system based on all config layers.
Expand All @@ -115,8 +111,7 @@ impl ConfigOpts {
let watch_layer = Self::cli_opts_layer_watch(cli_watch, build_layer);
let build_opts = watch_layer.build.unwrap_or_default();
let watch_opts = watch_layer.watch.unwrap_or_default();
let manifest = CargoMetadata::new(&build_opts.manifest).await?;
Ok(Arc::new(RtcWatch::new(manifest, build_opts, watch_opts)?))
Ok(Arc::new(RtcWatch::new(build_opts, watch_opts)?))
}

/// Extract the runtime config for the serve system based on all config layers.
Expand All @@ -130,8 +125,7 @@ impl ConfigOpts {
let build_opts = serve_layer.build.unwrap_or_default();
let watch_opts = serve_layer.watch.unwrap_or_default();
let serve_opts = serve_layer.serve.unwrap_or_default();
let manifest = CargoMetadata::new(&build_opts.manifest).await?;
Ok(Arc::new(RtcServe::new(manifest, build_opts, watch_opts, serve_opts, serve_layer.proxy)?))
Ok(Arc::new(RtcServe::new(build_opts, watch_opts, serve_opts, serve_layer.proxy)?))
}

/// Extract the runtime config for the clean system based on all config layers.
Expand All @@ -152,7 +146,6 @@ impl ConfigOpts {
target: cli.target,
release: cli.release,
dist: cli.dist,
manifest: cli.manifest,
public_url: cli.public_url,
};
let cfg_build = ConfigOpts {
Expand Down Expand Up @@ -244,11 +237,6 @@ impl ConfigOpts {
*dist = parent.join(&dist);
}
});
build.manifest.iter_mut().for_each(|manifest| {
if !manifest.is_absolute() {
*manifest = parent.join(&manifest);
}
});
});
cfg.watch.iter_mut().for_each(|watch| {
watch.ignore.iter_mut().for_each(|ignores_vec| {
Expand Down Expand Up @@ -292,7 +280,6 @@ impl ConfigOpts {
(Some(l), Some(mut g)) => {
g.target = g.target.or(l.target);
g.dist = g.dist.or(l.dist);
g.manifest = g.manifest.or(l.manifest);
g.public_url = g.public_url.or(l.public_url);
// NOTE: this can not be disabled in the cascade.
if l.release {
Expand Down
28 changes: 11 additions & 17 deletions src/config/rt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use std::sync::Arc;
use anyhow::{Context, Result};
use http_types::Url;

use crate::config::{CargoMetadata, ConfigOptsBuild, ConfigOptsClean, ConfigOptsProxy, ConfigOptsServe, ConfigOptsWatch};
use crate::config::{ConfigOptsBuild, ConfigOptsClean, ConfigOptsProxy, ConfigOptsServe, ConfigOptsWatch};

/// Runtime config for the build system.
#[derive(Clone, Debug)]
Expand All @@ -15,29 +15,24 @@ pub struct RtcBuild {
pub release: bool,
/// The output dir for all final assets.
pub dist: PathBuf,
/// The metadata of the associated cargo project being processed.
pub manifest: CargoMetadata,
/// The public URL from which assets are to be served.
pub public_url: String,
}

impl RtcBuild {
/// Construct a new instance.
pub(super) fn new(manifest: CargoMetadata, opts: ConfigOptsBuild) -> Result<Self> {
pub(super) fn new(opts: ConfigOptsBuild) -> Result<Self> {
let target = opts.target.clone().unwrap_or_else(|| "index.html".into());
let target_parent_dir = target
.parent()
.map(|path| path.to_owned())
.unwrap_or_else(|| PathBuf::from(std::path::MAIN_SEPARATOR.to_string()));
Ok(Self {
target: target
.canonicalize()
.with_context(|| format!("error getting canonical path to source HTML file {:?}", &target))?,
release: opts.release,
// Use a config defined value.
dist: opts.dist.unwrap_or_else(||
// Else fallback to the parent dir of the cargo target dir.
manifest.metadata.target_directory
.parent().map(|path| path.join("dist"))
// Else fallback to the "dist" dir of the CWD (this will practically never be hit).
.unwrap_or_else(|| "dist".into())),
manifest,
dist: opts.dist.unwrap_or_else(|| target_parent_dir.join("dist")),
public_url: opts.public_url.unwrap_or_else(|| "/".into()),
})
}
Expand All @@ -53,8 +48,8 @@ pub struct RtcWatch {
}

impl RtcWatch {
pub(super) fn new(manifest: CargoMetadata, build_opts: ConfigOptsBuild, opts: ConfigOptsWatch) -> Result<Self> {
let build = Arc::new(RtcBuild::new(manifest, build_opts)?);
pub(super) fn new(build_opts: ConfigOptsBuild, opts: ConfigOptsWatch) -> Result<Self> {
let build = Arc::new(RtcBuild::new(build_opts)?);
Ok(Self {
build,
ignore: opts.ignore.unwrap_or_default(),
Expand All @@ -81,10 +76,9 @@ pub struct RtcServe {

impl RtcServe {
pub(super) fn new(
manifest: CargoMetadata, build_opts: ConfigOptsBuild, watch_opts: ConfigOptsWatch, opts: ConfigOptsServe,
proxies: Option<Vec<ConfigOptsProxy>>,
build_opts: ConfigOptsBuild, watch_opts: ConfigOptsWatch, opts: ConfigOptsServe, proxies: Option<Vec<ConfigOptsProxy>>,
) -> Result<Self> {
let watch = Arc::new(RtcWatch::new(manifest, build_opts, watch_opts)?);
let watch = Arc::new(RtcWatch::new(build_opts, watch_opts)?);
Ok(Self {
watch,
port: opts.port.unwrap_or(8080),
Expand Down
87 changes: 0 additions & 87 deletions src/pipelines/assets/mod.rs

This file was deleted.

Loading

0 comments on commit 7701795

Please sign in to comment.