#[cfg(not(feature = "native"))]
use vize_carton::ToCompactString;
use vize_carton::{FxHashMap, String};
#[cfg(feature = "native")]
mod parser;
mod scoped;
#[cfg(test)]
mod tests;
mod transform;
use serde::{Deserialize, Serialize};
use vize_carton::Bump;
use crate::types::SfcStyleBlock;
use self::scoped::apply_scoped_css;
use self::transform::extract_and_transform_v_bind;
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CssCompileOptions {
#[serde(default)]
pub scope_id: Option<String>,
#[serde(default)]
pub scoped: bool,
#[serde(default)]
pub minify: bool,
#[serde(default)]
pub source_map: bool,
#[serde(default)]
pub targets: Option<CssTargets>,
#[serde(default)]
pub filename: Option<String>,
#[serde(default)]
pub custom_media: bool,
#[serde(default)]
pub css_modules: bool,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CssTargets {
#[serde(default)]
pub chrome: Option<u32>,
#[serde(default)]
pub firefox: Option<u32>,
#[serde(default)]
pub safari: Option<u32>,
#[serde(default)]
pub edge: Option<u32>,
#[serde(default)]
pub ios: Option<u32>,
#[serde(default)]
pub android: Option<u32>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CssModuleExport {
pub name: String,
pub is_referenced: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CssCompileResult {
pub code: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub map: Option<String>,
#[serde(default)]
pub css_vars: Vec<String>,
#[serde(default)]
pub errors: Vec<String>,
#[serde(default)]
pub warnings: Vec<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub exports: Option<FxHashMap<String, CssModuleExport>>,
}
#[cfg(feature = "native")]
pub fn compile_css(css: &str, options: &CssCompileOptions) -> CssCompileResult {
let bump = Bump::new();
let filename = options.filename.as_deref().unwrap_or("style.css");
let (processed_css, css_vars) = extract_and_transform_v_bind(&bump, css);
let scoped_css = if options.scoped {
if let Some(ref scope_id) = options.scope_id {
apply_scoped_css(&bump, processed_css, scope_id)
} else {
processed_css
}
} else {
processed_css
};
let targets = options
.targets
.as_ref()
.map(|t| t.to_lightningcss_targets())
.unwrap_or_default();
let result = parser::compile_css_internal(
scoped_css,
filename,
options.minify,
targets,
options.custom_media,
options.css_modules,
);
CssCompileResult {
code: result.code,
map: None,
css_vars,
errors: result.errors,
warnings: vec![],
exports: result.exports,
}
}
#[cfg(not(feature = "native"))]
pub fn compile_css(css: &str, options: &CssCompileOptions) -> CssCompileResult {
let bump = Bump::new();
let (processed_css, css_vars) = extract_and_transform_v_bind(&bump, css);
let scoped_css = if options.scoped {
if let Some(ref scope_id) = options.scope_id {
apply_scoped_css(&bump, processed_css, scope_id)
} else {
processed_css
}
} else {
processed_css
};
CssCompileResult {
code: scoped_css.to_compact_string(),
map: None,
css_vars,
errors: vec![],
warnings: vec![],
exports: None,
}
}
#[cfg(feature = "native")]
pub fn bundle_css(entry_path: &str, options: &CssCompileOptions) -> CssCompileResult {
let targets = options
.targets
.as_ref()
.map(|t| t.to_lightningcss_targets())
.unwrap_or_default();
let result = parser::bundle_css_internal(
entry_path,
options.minify,
targets,
options.css_modules,
options.custom_media,
);
CssCompileResult {
code: result.code,
map: None,
css_vars: vec![],
errors: result.errors,
warnings: vec![],
exports: result.exports,
}
}
#[cfg(not(feature = "native"))]
pub fn bundle_css(_entry_path: &str, _options: &CssCompileOptions) -> CssCompileResult {
CssCompileResult {
code: String::default(),
map: None,
css_vars: vec![],
errors: vec![String::from("CSS bundling requires the `native` feature")],
warnings: vec![],
exports: None,
}
}
pub fn compile_style_block(style: &SfcStyleBlock, options: &CssCompileOptions) -> CssCompileResult {
let mut opts = options.clone();
opts.scoped = style.scoped || opts.scoped;
compile_css(&style.content, &opts)
}
#[cfg(feature = "native")]
impl CssTargets {
pub(crate) fn to_lightningcss_targets(&self) -> lightningcss::targets::Targets {
let mut browsers = lightningcss::targets::Browsers::default();
if let Some(v) = self.chrome {
browsers.chrome = Some(parser::version_to_u32(v));
}
if let Some(v) = self.firefox {
browsers.firefox = Some(parser::version_to_u32(v));
}
if let Some(v) = self.safari {
browsers.safari = Some(parser::version_to_u32(v));
}
if let Some(v) = self.edge {
browsers.edge = Some(parser::version_to_u32(v));
}
if let Some(v) = self.ios {
browsers.ios_saf = Some(parser::version_to_u32(v));
}
if let Some(v) = self.android {
browsers.android = Some(parser::version_to_u32(v));
}
lightningcss::targets::Targets::from(browsers)
}
}