139 lines
4.3 KiB
Rust
139 lines
4.3 KiB
Rust
use crate::config::SabryConfig;
|
|
|
|
/// Convenience wrapper on Grass and Lightningcss
|
|
pub struct CompilerAdapter {
|
|
#[allow(dead_code)] // without 'compile' feature, still want to set this
|
|
config: SabryConfig,
|
|
}
|
|
|
|
impl CompilerAdapter {
|
|
pub fn new(config: SabryConfig) -> Self {
|
|
Self { config }
|
|
}
|
|
|
|
/// Compile given SASS/SCSS into CSS
|
|
/// with respect to self.config
|
|
#[cfg(feature = "compile")]
|
|
pub fn compile_module(
|
|
&self,
|
|
syntax: crate::syntax::ostrta::OneSyntaxToRuleThemAll,
|
|
code: &str,
|
|
) -> Result<String, SabryCompilerError> {
|
|
let options = grass::Options::from(&self.config).input_syntax(syntax.into());
|
|
|
|
let css = self.lightningcss(&grass::from_string(code, &options)?)?;
|
|
|
|
Ok(css)
|
|
}
|
|
|
|
/// Perform lightningcss transformations on given css
|
|
/// with respect to self.config.css.minify on minification
|
|
#[cfg(feature = "compile")]
|
|
pub fn lightningcss(&self, css: &str) -> Result<String, SabryCompilerError> {
|
|
use lightningcss::{printer::PrinterOptions, stylesheet::MinifyOptions, targets::Targets};
|
|
|
|
let mut lightsheet = match lightningcss::stylesheet::StyleSheet::parse(
|
|
css,
|
|
lightningcss::stylesheet::ParserOptions::default(),
|
|
) {
|
|
Ok(s) => s,
|
|
Err(e) => {
|
|
return Err(SabryCompilerError::LightParse {
|
|
kind: e.kind.to_string(),
|
|
loc: e.loc,
|
|
})
|
|
}
|
|
};
|
|
|
|
let targets = Targets {
|
|
browsers: Some(self.config.lightningcss.targets.clone().into()),
|
|
..Default::default()
|
|
};
|
|
|
|
let printer_options = PrinterOptions {
|
|
minify: self.config.css.minify,
|
|
targets,
|
|
..Default::default()
|
|
};
|
|
|
|
let minify_options = MinifyOptions {
|
|
targets,
|
|
..Default::default()
|
|
};
|
|
|
|
if self.config.css.minify {
|
|
lightsheet.minify(minify_options)?
|
|
}
|
|
|
|
let css = match lightsheet.to_css(printer_options) {
|
|
Ok(c) => {
|
|
drop(lightsheet);
|
|
c.code
|
|
}
|
|
Err(e) => return Err(SabryCompilerError::LightPrint(e)),
|
|
};
|
|
|
|
Ok(css)
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "compile")]
|
|
impl From<&SabryConfig> for grass::Options<'_> {
|
|
fn from(config: &SabryConfig) -> Self {
|
|
let mut options = grass::Options::default();
|
|
options = options.style(if config.css.minify {
|
|
grass::OutputStyle::Compressed
|
|
} else {
|
|
grass::OutputStyle::Expanded
|
|
});
|
|
options = options.load_path(&config.sass.intermediate_dir);
|
|
|
|
options
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "compile")]
|
|
#[derive(thiserror::Error)]
|
|
pub enum SabryCompilerError {
|
|
#[error("Could not compile into CSS with grass")]
|
|
GrassCompile(#[from] Box<grass::Error>),
|
|
#[error("Could not print compiled CSS with lightningcss")]
|
|
LightPrint(#[from] lightningcss::error::Error<lightningcss::error::PrinterErrorKind>),
|
|
#[error("Could not minify CSS with lightningcss")]
|
|
LightMinify(#[from] lightningcss::error::Error<lightningcss::error::MinifyErrorKind>),
|
|
#[error("Could not parse CSS with lightningcss")]
|
|
LightParse {
|
|
kind: String,
|
|
loc: Option<lightningcss::error::ErrorLocation>,
|
|
},
|
|
}
|
|
|
|
#[cfg(feature = "compile")]
|
|
impl std::fmt::Debug for SabryCompilerError {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
let explain = match self {
|
|
Self::LightPrint(e) => format!(
|
|
"{} at {:?}",
|
|
e.kind,
|
|
e.loc
|
|
.clone()
|
|
.map(|l| format!("file {}, line {}, col {}", l.filename, l.line, l.column))
|
|
),
|
|
Self::GrassCompile(err) => format!("{err}"),
|
|
Self::LightMinify(e) => format!(
|
|
"{} at {:?}",
|
|
e.kind,
|
|
e.loc
|
|
.clone()
|
|
.map(|l| format!("file {}, line {}, col {}", l.filename, l.line, l.column))
|
|
),
|
|
Self::LightParse { kind, loc } => format!(
|
|
"{kind} at {:?}",
|
|
loc.clone()
|
|
.map(|l| format!("file {}, line {}, col {}", l.filename, l.line, l.column))
|
|
),
|
|
};
|
|
|
|
write!(f, "{explain}")
|
|
}
|
|
}
|