cobalt/cobalt_model/
sass.rs1use std::ffi;
2use std::path;
3
4use serde::{Deserialize, Serialize};
5
6use super::files;
7use crate::cobalt_model::Minify;
8use crate::error::Result;
9pub(crate) use cobalt_config::SassOutputStyle;
10
11#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)]
12#[serde(deny_unknown_fields, default)]
13pub struct SassBuilder {
14 pub import_dir: path::PathBuf,
15 pub style: SassOutputStyle,
16}
17
18impl SassBuilder {
19 pub fn from_config(config: cobalt_config::Sass, source: &path::Path) -> Self {
20 Self {
21 style: config.style,
22 import_dir: source.join(config.import_dir),
23 }
24 }
25
26 pub fn build(self) -> SassCompiler {
27 let Self { import_dir, style } = self;
28 SassCompiler { import_dir, style }
29 }
30}
31
32#[derive(Debug, PartialEq, Eq)]
33pub struct SassCompiler {
34 import_dir: path::PathBuf,
35 style: SassOutputStyle,
36}
37
38impl SassCompiler {
39 pub fn compile_file<S: AsRef<path::Path>, D: AsRef<path::Path>, F: AsRef<path::Path>>(
40 &self,
41 source: S,
42 dest: D,
43 file_path: F,
44 minify: &Minify,
45 ) -> Result<()> {
46 self.compile_sass_internal(source.as_ref(), dest.as_ref(), file_path.as_ref(), minify)
47 }
48
49 #[cfg(feature = "sass")]
50 fn compile_sass_internal(
51 &self,
52 source: &path::Path,
53 dest: &path::Path,
54 file_path: &path::Path,
55 minify: &Minify,
56 ) -> Result<()> {
57 let sass_opts = grass::Options::default()
58 .style(match self.style {
59 SassOutputStyle::Nested | SassOutputStyle::Expanded => grass::OutputStyle::Expanded,
60 SassOutputStyle::Compact | SassOutputStyle::Compressed => {
61 grass::OutputStyle::Compressed
62 }
63 })
64 .load_path(&self.import_dir);
65 let content = if let Some(file_path) = file_path.to_str() {
66 grass::from_path(file_path, &sass_opts)?
67 } else {
68 let raw = std::fs::read_to_string(file_path)?;
69 grass::from_string(raw, &sass_opts)?
70 };
71
72 let rel_src = file_path
73 .strip_prefix(source)
74 .expect("file was found under the root");
75 let mut dest_file = dest.join(rel_src);
76 dest_file.set_extension("css");
77
78 #[cfg(feature = "html-minifier")]
79 let content = if minify.css {
80 use html_minifier::css::minify;
81 minify(&content)
82 .map_err(|e| {
83 anyhow::format_err!(
84 "Could not minify saas file {} error {}",
85 source.to_string_lossy(),
86 e
87 )
88 })?
89 .to_string()
90 } else {
91 content
92 };
93
94 files::write_document_file(content, dest_file)
95 }
96
97 #[cfg(not(feature = "sass"))]
98 fn compile_sass_internal(
99 &self,
100 source: &path::Path,
101 dest: &path::Path,
102 file_path: &path::Path,
103 minify: &Minify,
104 ) -> Result<()> {
105 let rel_src = file_path
106 .strip_prefix(source)
107 .expect("file was found under the root");
108 let dest_file = dest.join(rel_src);
109 files::copy_file(file_path, &dest_file)
110 }
111}
112
113impl Default for SassCompiler {
114 fn default() -> Self {
115 SassBuilder::default().build()
116 }
117}
118
119pub(crate) fn is_sass_file(file_path: &path::Path) -> bool {
120 file_path.extension() == Some(ffi::OsStr::new("scss"))
121 || file_path.extension() == Some(ffi::OsStr::new("sass"))
122}