1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
//! The [CSS Modules] project defines CSS Modules as:
//!
//! > A **CSS Module** is a CSS file in which all class names and animation names are scoped locally by default.
//!
//! This implementation is however currently immature and what parsing we do have is very naively implemented. As a result, currently only class names are locally scoped and the following work is in progress:
//!
//! - Locally scoped animation names
//! - Inlining `url()` and `@import` statements
//!
//! ## Usage
//!
//! A [`Stylesheet`] can be constructed manually:
//!
//! ```
//! use css_modules::stylesheet::*;
//!
//! let css = Stylesheet::new("my_module", ".myStyles {}");
//! ```
//!
//! Or if you would prefer to use automatic module naming based on source code, through a macro:
//!
//! ```
//! use css_modules::*;
//!
//! let css = css_module!(".myStyles {}");
//! ```
//!
//! The same as above, but from a file relative to the current source file:
//!
//! ```
//! use css_modules::*;
//!
//! let css = include_css_module!("test.css");
//! ```
//!
//! Which is the equivelent of doing:
//!
//! ```
//! use css_modules::*;
//!
//! let css = css_module!(include_str!("test.css"));
//! ```
//!
//! [CSS Modules]: https://github.com/css-modules/css-modules
pub mod grammar;
pub mod stylesheet;
/// Generate a css module name based on the location from where it was called.
pub fn css_module_name() -> String {
use backtrace::{Backtrace, BacktraceFrame, BacktraceSymbol};
use lazy_static::lazy_static;
use regex::Regex;
lazy_static! {
static ref NAME_FORMATTER: Regex = Regex::new(r"[^\w]+").unwrap();
}
fn previous_symbol(level: u32) -> Option<BacktraceSymbol> {
let (trace, curr_file, curr_line) = (Backtrace::new(), file!(), line!());
let frames = trace.frames();
frames
.iter()
.flat_map(BacktraceFrame::symbols)
.skip_while(|s| {
s.filename()
.map(|p| !p.ends_with(curr_file))
.unwrap_or(true)
|| s.lineno() != Some(curr_line)
})
.nth(1 + level as usize)
.cloned()
}
let symbol = previous_symbol(1);
let name = symbol.as_ref().and_then(BacktraceSymbol::name);
if let Some(name) = name {
NAME_FORMATTER
.replace_all(&format!("{:?}", name), "_")
.to_string()
} else {
"unknown_module".to_string()
}
}
/// Make a [`Stylesheet`] from a string.
///
/// ```
/// use css_modules::*;
///
/// let css = css_module!(".myClass { font-weight: bold; color: red; }");
///
/// // Get the localised class name:
/// css.id("myClass").unwrap();
///
/// // Get the module stylesheet as a string:
/// format!("{}", css);
/// ```
#[macro_export]
macro_rules! css_module {
($stylesheet:expr) => {{
use $crate::stylesheet::*;
match Stylesheet::new(&css_module_name(), $stylesheet) {
Ok(css) => css,
Err(error) => panic!("{}", error),
}
}};
($name:expr, $stylesheet:expr) => {{
use $crate::stylesheet::*;
match Stylesheet::new($name, $stylesheet) {
Ok(css) => css,
Err(error) => panic!("{}", error),
}
}};
}
/// Make a [`Stylesheet`] from a file relative to the current source file.
///
/// ```
/// use css_modules::*;
///
/// let css = include_css_module!("test.css");
///
/// // Get the localised class name:
/// css.id("myClass").unwrap();
///
/// // Get the module stylesheet as a string:
/// format!("{}", css);
/// ```
#[macro_export]
macro_rules! include_css_module {
($file:expr) => {{
css_module!(include_str!($file))
}};
($name:expr, $file:expr) => {{
css_module!($name, include_str!($file))
}};
}