Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add tailwindcss-extra for bundled DaisyUI to avoid Node dependency. #928

Merged
merged 2 commits into from
Jan 13, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions src/pipelines/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ mod js;
mod rust;
mod sass;
mod tailwind_css;
mod tailwind_css_extra;

pub use html::HtmlPipeline;

Expand All @@ -28,6 +29,7 @@ use crate::{
rust::{RustApp, RustAppOutput},
sass::{Sass, SassOutput},
tailwind_css::{TailwindCss, TailwindCssOutput},
tailwind_css_extra::{TailwindCssExtra, TailwindCssExtraOutput},
},
processing::minify::{minify_css, minify_js},
};
Expand Down Expand Up @@ -78,6 +80,7 @@ pub enum TrunkAsset {
Css(Css),
Sass(Sass),
TailwindCss(TailwindCss),
TailwindCssExtra(TailwindCssExtra),
Js(Js),
Icon(Icon),
Inline(Inline),
Expand Down Expand Up @@ -120,6 +123,9 @@ impl TrunkAsset {
TailwindCss::TYPE_TAILWIND_CSS => {
Self::TailwindCss(TailwindCss::new(cfg, html_dir, attrs, id).await?)
}
TailwindCssExtra::TYPE_TAILWIND_CSS_EXTRA => Self::TailwindCssExtra(
TailwindCssExtra::new(cfg, html_dir, attrs, id).await?,
),
_ => bail!(
r#"unknown <link data-trunk .../> attr value `rel="{}"`; please ensure the value is lowercase and is a supported asset type"#,
rel
Expand All @@ -138,6 +144,7 @@ impl TrunkAsset {
Self::Css(inner) => inner.spawn(),
Self::Sass(inner) => inner.spawn(),
Self::TailwindCss(inner) => inner.spawn(),
Self::TailwindCssExtra(inner) => inner.spawn(),
Self::Js(inner) => inner.spawn(),
Self::Icon(inner) => inner.spawn(),
Self::Inline(inner) => inner.spawn(),
Expand All @@ -153,6 +160,7 @@ pub enum TrunkAssetPipelineOutput {
Css(CssOutput),
Sass(SassOutput),
TailwindCss(TailwindCssOutput),
TailwindCssExtra(TailwindCssExtraOutput),
Js(JsOutput),
Icon(IconOutput),
Inline(InlineOutput),
Expand All @@ -168,6 +176,7 @@ impl TrunkAssetPipelineOutput {
TrunkAssetPipelineOutput::Css(out) => out.finalize(dom).await,
TrunkAssetPipelineOutput::Sass(out) => out.finalize(dom).await,
TrunkAssetPipelineOutput::TailwindCss(out) => out.finalize(dom).await,
TrunkAssetPipelineOutput::TailwindCssExtra(out) => out.finalize(dom).await,
TrunkAssetPipelineOutput::Js(out) => out.finalize(dom).await,
TrunkAssetPipelineOutput::Icon(out) => out.finalize(dom).await,
TrunkAssetPipelineOutput::Inline(out) => out.finalize(dom).await,
Expand Down
210 changes: 210 additions & 0 deletions src/pipelines/tailwind_css_extra.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
//! Tailwind CSS asset pipeline.

use super::{
data_target_path, AssetFile, AttrWriter, Attrs, TrunkAssetPipelineOutput, ATTR_CONFIG,
ATTR_HREF, ATTR_INLINE, ATTR_NO_MINIFY,
};
use crate::{
common::{self, dist_relative, html_rewrite::Document, nonce, target_path},
config::rt::RtcBuild,
processing::integrity::{IntegrityType, OutputDigest},
tools::{self, Application},
};
use anyhow::{Context, Result};
use std::{path::PathBuf, sync::Arc};
use tokio::{fs, task::JoinHandle};

/// A tailwind css asset pipeline.
pub struct TailwindCssExtra {
/// The ID of this pipeline's source HTML element.
id: usize,
/// Runtime build config.
cfg: Arc<RtcBuild>,
/// The asset file being processed.
asset: AssetFile,
/// If the specified tailwind css file should be inlined.
use_inline: bool,
/// E.g. `disabled`, `id="..."`
attrs: Attrs,
/// The required integrity setting
integrity: IntegrityType,
/// Whether to minify or not
no_minify: bool,
/// Optional target path inside the dist dir.
target_path: Option<PathBuf>,
/// Optional tailwind config to use.
tailwind_config: Option<String>,
}

impl TailwindCssExtra {
pub const TYPE_TAILWIND_CSS_EXTRA: &'static str = "tailwind-css-extra";

pub async fn new(
cfg: Arc<RtcBuild>,
html_dir: Arc<PathBuf>,
attrs: Attrs,
id: usize,
) -> Result<Self> {
// Build the path to the target asset.
let href_attr = attrs.get(ATTR_HREF).context(
r#"required attr `href` missing for <link data-trunk rel="tailwind-css-extra" .../> element"#,
)?;
let tailwind_config = attrs.get(ATTR_CONFIG).cloned();
let mut path = PathBuf::new();
path.extend(href_attr.split('/'));
let asset = AssetFile::new(&html_dir, path).await?;
let use_inline = attrs.contains_key(ATTR_INLINE);

let integrity = IntegrityType::from_attrs(&attrs, &cfg)?;
let no_minify = attrs.contains_key(ATTR_NO_MINIFY);
let target_path = data_target_path(&attrs)?;

Ok(Self {
id,
cfg,
asset,
use_inline,
integrity,
attrs,
no_minify,
target_path,
tailwind_config,
})
}

/// Spawn the pipeline for this asset type.
#[tracing::instrument(level = "trace", skip(self))]
pub fn spawn(self) -> JoinHandle<Result<TrunkAssetPipelineOutput>> {
tokio::spawn(self.run())
}

/// Run this pipeline.
#[tracing::instrument(level = "trace", skip(self))]
async fn run(self) -> Result<TrunkAssetPipelineOutput> {
let version = self.cfg.tools.tailwindcss.as_deref();
let tailwind = tools::get(
Application::TailwindCssExtra,
version,
self.cfg.offline,
&self.cfg.client_options(),
)
.await?;

// Compile the target tailwind css file.
let path_str = dunce::simplified(&self.asset.path).display().to_string();
let file_name = format!("{}.css", &self.asset.file_stem.to_string_lossy());
let file_path = dunce::simplified(&self.cfg.staging_dist.join(&file_name))
.display()
.to_string();

let mut args = vec!["--input", &path_str, "--output", &file_path];

if let Some(tailwind_config) = self.tailwind_config.as_ref() {
args.push("--config");
args.push(tailwind_config);
}

if self.cfg.minify_asset(self.no_minify) {
args.push("--minify");
}

let rel_path = common::strip_prefix(&self.asset.path);
tracing::debug!(path = ?rel_path, "compiling tailwind css");

common::run_command(
Application::TailwindCssExtra.name(),
&tailwind,
&args,
&self.cfg.core.working_directory,
)
.await?;

let css = fs::read_to_string(&file_path).await?;
fs::remove_file(&file_path).await?;

// Check if the specified tailwind css file should be inlined.
let css_ref = if self.use_inline {
// Avoid writing any files, return the CSS as a String.
CssExtraRef::Inline(css)
} else {
// Hash the contents to generate a file name, and then write the contents to the dist
// dir.
let hash = seahash::hash(css.as_bytes());
let file_name = self
.cfg
.filehash
.then(|| format!("{}-{:x}.css", &self.asset.file_stem.to_string_lossy(), hash))
.unwrap_or(file_name);

let result_dir =
target_path(&self.cfg.staging_dist, self.target_path.as_deref(), None).await?;
let file_path = result_dir.join(&file_name);
let file_href = dist_relative(&self.cfg.staging_dist, &file_path)?;

let integrity = OutputDigest::generate_from(self.integrity, css.as_bytes());

// Write the generated CSS to the filesystem.
fs::write(&file_path, css)
.await
.context("error writing tailwind css pipeline output")?;

// Generate a hashed reference to the new CSS file.
CssExtraRef::File(file_href, integrity)
};

tracing::debug!(path = ?rel_path, "finished compiling tailwind css");
Ok(TrunkAssetPipelineOutput::TailwindCssExtra(
TailwindCssExtraOutput {
cfg: self.cfg.clone(),
id: self.id,
css_ref,
attrs: self.attrs,
},
))
}
}

/// The output of a Tailwind CSS build pipeline.
pub struct TailwindCssExtraOutput {
/// The runtime build config.
pub cfg: Arc<RtcBuild>,
/// The ID of this pipeline.
pub id: usize,
/// Data on the finalized output file.
pub css_ref: CssExtraRef,
/// The other attributes copied over from the original.
pub attrs: Attrs,
}

/// The resulting CSS of the Tailwind CSS compilation.
pub enum CssExtraRef {
/// CSS to be inlined (for `data-inline`).
Inline(String),
/// A hashed file reference to a CSS file (default).
File(String, OutputDigest),
}

impl TailwindCssExtraOutput {
pub async fn finalize(self, dom: &mut Document) -> Result<()> {
let html = match self.css_ref {
// Insert the inlined CSS into a `<style>` tag.
CssExtraRef::Inline(css) => format!(
r#"<style {attrs} nonce="{}">{css}</style>"#,
nonce(),
attrs = AttrWriter::new(&self.attrs, AttrWriter::EXCLUDE_CSS_INLINE)
),
// Link to the CSS file.
CssExtraRef::File(file, integrity) => {
let mut attrs = self.attrs.clone();
integrity.insert_into(&mut attrs);

format!(
r#"<link rel="stylesheet" href="{base}{file}"{attrs}/>"#,
base = &self.cfg.public_url,
attrs = AttrWriter::new(&attrs, AttrWriter::EXCLUDE_CSS_LINK)
)
}
};
dom.replace_with_html(&super::trunk_id_selector(self.id), &html)
}
}
27 changes: 27 additions & 0 deletions src/tools.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ pub enum Application {
Sass,
/// tailwindcss for generating css
TailwindCss,
/// tailwindcss-extra for generating css with DaisyUI bundled.
TailwindCssExtra,
/// wasm-bindgen for generating the JS bindings.
WasmBindgen,
/// wasm-opt to improve performance and size of the output file further.
Expand Down Expand Up @@ -48,6 +50,7 @@ impl Application {
match self {
Self::Sass => "sass",
Self::TailwindCss => "tailwindcss",
Self::TailwindCssExtra => "tailwindcss-extra",
Self::WasmBindgen => "wasm-bindgen",
Self::WasmOpt => "wasm-opt",
}
Expand All @@ -59,13 +62,15 @@ impl Application {
match self {
Self::Sass => "sass.bat",
Self::TailwindCss => "tailwindcss.exe",
Self::TailwindCssExtra => "tailwindcss-extra.exe",
Self::WasmBindgen => "wasm-bindgen.exe",
Self::WasmOpt => "bin/wasm-opt.exe",
}
} else {
match self {
Self::Sass => "sass",
Self::TailwindCss => "tailwindcss",
Self::TailwindCssExtra => "tailwindcss-extra",
Self::WasmBindgen => "wasm-bindgen",
Self::WasmOpt => "bin/wasm-opt",
}
Expand All @@ -83,6 +88,7 @@ impl Application {
}
}
Self::TailwindCss => &[],
Self::TailwindCssExtra => &[],
Self::WasmBindgen => &[],
Self::WasmOpt => {
if cfg!(target_os = "macos") {
Expand All @@ -99,6 +105,7 @@ impl Application {
match self {
Self::Sass => "1.69.5",
Self::TailwindCss => "3.3.5",
Self::TailwindCssExtra => "1.7.25",
Self::WasmBindgen => "0.2.89",
Self::WasmOpt => "version_116",
}
Expand Down Expand Up @@ -138,6 +145,13 @@ impl Application {
("macos" | "linux", "aarch64") => format!("https://github.com/tailwindlabs/tailwindcss/releases/download/v{version}/tailwindcss-{target_os}-arm64"),
_ => bail!("Unable to download tailwindcss for {target_os} {target_arch}")
},

Self::TailwindCssExtra => match (target_os, target_arch) {
("windows", "x86_64") => format!("https://github.com/dobicinaitis/tailwind-cli-extra/releases/download/v{version}/tailwindcss-extra-windows-x64.exe"),
("macos" | "linux", "x86_64") => format!("https://github.com/dobicinaitis/tailwind-cli-extra/releases/download/v{version}/tailwindcss-extra-{target_os}-x64"),
("macos" | "linux", "aarch64") => format!("https://github.com/dobicinaitis/tailwind-cli-extra/releases/download/v{version}/tailwindcss-extra-{target_os}-arm64"),
_ => bail!("Unable to download tailwindcss for {target_os} {target_arch}")
},

Self::WasmBindgen => match (target_os, target_arch) {
("windows", "x86_64") => format!("https://github.com/rustwasm/wasm-bindgen/releases/download/{version}/wasm-bindgen-{version}-x86_64-pc-windows-msvc.tar.gz"),
Expand All @@ -160,6 +174,7 @@ impl Application {
match self {
Application::Sass => "--version",
Application::TailwindCss => "--help",
Application::TailwindCssExtra => "--help",
Application::WasmBindgen => "--version",
Application::WasmOpt => "--version",
}
Expand All @@ -180,6 +195,12 @@ impl Application {
.and_then(|s| s.split(" v").nth(1))
.with_context(|| format!("missing or malformed version output: {}", text))?
.to_owned(),
Application::TailwindCssExtra => text
.lines()
.find(|s| !str::is_empty(s))
.and_then(|s| s.split(" v").nth(1))
.with_context(|| format!("missing or malformed version output: {}", text))?
.to_owned(),
Application::WasmBindgen => text
.split(' ')
.nth(1)
Expand Down Expand Up @@ -791,4 +812,10 @@ mod tests {
"tailwindcss v3.3.2",
"3.3.2"
);
table_test_format_version!(
tailwindcss_extra_pre_compiled,
Application::TailwindCssExtra,
"tailwindcss-extra v1.7.25",
"1.7.25"
);
}
Loading