css.rs 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. use std::path::Path;
  2. use anyhow::Context;
  3. use codemap::SpanLoc;
  4. use grass::OutputStyle;
  5. use lightningcss::{
  6. printer::PrinterOptions,
  7. stylesheet::{MinifyOptions, ParserOptions, StyleSheet},
  8. targets::{Browsers, Targets},
  9. };
  10. use manganis_core::CssAssetOptions;
  11. pub(crate) fn process_css(
  12. css_options: &CssAssetOptions,
  13. source: &Path,
  14. output_path: &Path,
  15. ) -> anyhow::Result<()> {
  16. let css = std::fs::read_to_string(source)?;
  17. let css = if css_options.minified() {
  18. // Try to minify the css. If we fail, log the error and use the unminified css
  19. match minify_css(&css) {
  20. Ok(minified) => minified,
  21. Err(err) => {
  22. tracing::error!(
  23. "Failed to minify css; Falling back to unminified css. Error: {}",
  24. err
  25. );
  26. css
  27. }
  28. }
  29. } else {
  30. css
  31. };
  32. std::fs::write(output_path, css).with_context(|| {
  33. format!(
  34. "Failed to write css to output location: {}",
  35. output_path.display()
  36. )
  37. })?;
  38. Ok(())
  39. }
  40. pub(crate) fn minify_css(css: &str) -> anyhow::Result<String> {
  41. let options = ParserOptions {
  42. error_recovery: true,
  43. ..Default::default()
  44. };
  45. let mut stylesheet = StyleSheet::parse(css, options).map_err(|err| err.into_owned())?;
  46. // We load the browser list from the standard browser list file or use the browserslist default if we don't find any
  47. // settings. Without the browser lists default, lightningcss will default to supporting only the newest versions of
  48. // browsers.
  49. let browsers_list = match Browsers::load_browserslist()? {
  50. Some(browsers) => Some(browsers),
  51. None => {
  52. Browsers::from_browserslist(["defaults"]).expect("borwserslists should have defaults")
  53. }
  54. };
  55. let targets = Targets {
  56. browsers: browsers_list,
  57. ..Default::default()
  58. };
  59. stylesheet.minify(MinifyOptions {
  60. targets,
  61. ..Default::default()
  62. })?;
  63. let printer = PrinterOptions {
  64. targets,
  65. minify: true,
  66. ..Default::default()
  67. };
  68. let res = stylesheet.to_css(printer)?;
  69. Ok(res.code)
  70. }
  71. /// Process an scss/sass file into css.
  72. pub(crate) fn process_scss(
  73. scss_options: &CssAssetOptions,
  74. source: &Path,
  75. output_path: &Path,
  76. ) -> anyhow::Result<()> {
  77. let style = match scss_options.minified() {
  78. true => OutputStyle::Compressed,
  79. false => OutputStyle::Expanded,
  80. };
  81. let options = grass::Options::default()
  82. .style(style)
  83. .quiet(false)
  84. .logger(&ScssLogger {});
  85. let css = grass::from_path(source, &options)?;
  86. let minified = minify_css(&css)?;
  87. std::fs::write(output_path, minified).with_context(|| {
  88. format!(
  89. "Failed to write css to output location: {}",
  90. output_path.display()
  91. )
  92. })?;
  93. Ok(())
  94. }
  95. /// Logger for Grass that re-uses their StdLogger formatting but with tracing.
  96. #[derive(Debug)]
  97. struct ScssLogger {}
  98. impl grass::Logger for ScssLogger {
  99. fn debug(&self, location: SpanLoc, message: &str) {
  100. tracing::debug!(
  101. "{}:{} DEBUG: {}",
  102. location.file.name(),
  103. location.begin.line + 1,
  104. message
  105. );
  106. }
  107. fn warn(&self, location: SpanLoc, message: &str) {
  108. tracing::warn!(
  109. "Warning: {}\n ./{}:{}:{}",
  110. message,
  111. location.file.name(),
  112. location.begin.line + 1,
  113. location.begin.column + 1
  114. );
  115. }
  116. }