Skip to content

Commit

Permalink
Add tailwindcss-extra for bundled DaisyUI to avoid Node dependency.
Browse files Browse the repository at this point in the history
  • Loading branch information
opensourcecheemsburgers authored and ctron committed Jan 13, 2025
1 parent 4fd01b4 commit 90d8c79
Show file tree
Hide file tree
Showing 3 changed files with 246 additions and 0 deletions.
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 @@ -122,6 +125,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 @@ -140,6 +146,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 @@ -155,6 +162,7 @@ pub enum TrunkAssetPipelineOutput {
Css(CssOutput),
Sass(SassOutput),
TailwindCss(TailwindCssOutput),
TailwindCssExtra(TailwindCssExtraOutput),
Js(JsOutput),
Icon(IconOutput),
Inline(InlineOutput),
Expand All @@ -170,6 +178,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"
);
}

0 comments on commit 90d8c79

Please sign in to comment.