@prospective.co/procss 0.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +45 -0
  3. package/package.json +31 -0
  4. package/src/ast/flat_ruleset.rs +52 -0
  5. package/src/ast/ruleset/mod.rs +229 -0
  6. package/src/ast/ruleset/rule.rs +114 -0
  7. package/src/ast/selector/attribute.rs +90 -0
  8. package/src/ast/selector/combinator.rs +58 -0
  9. package/src/ast/selector/mod.rs +115 -0
  10. package/src/ast/selector/selector_path.rs +230 -0
  11. package/src/ast/selector/selector_term.rs +325 -0
  12. package/src/ast/token/mod.rs +18 -0
  13. package/src/ast/token/space.rs +157 -0
  14. package/src/ast/token/string.rs +59 -0
  15. package/src/ast/token/symbol.rs +32 -0
  16. package/src/ast/tree_ruleset.rs +252 -0
  17. package/src/ast.rs +216 -0
  18. package/src/builder.rs +189 -0
  19. package/src/js_builder.rs +59 -0
  20. package/src/lib.rs +151 -0
  21. package/src/main.rs +40 -0
  22. package/src/parser.rs +36 -0
  23. package/src/render.rs +84 -0
  24. package/src/transform.rs +14 -0
  25. package/src/transformers/apply_import.rs +53 -0
  26. package/src/transformers/apply_mixin.rs +73 -0
  27. package/src/transformers/apply_var.rs +41 -0
  28. package/src/transformers/dedupe.rs +35 -0
  29. package/src/transformers/filter_refs.rs +27 -0
  30. package/src/transformers/flat_self.rs +30 -0
  31. package/src/transformers/inline_url.rs +52 -0
  32. package/src/transformers/mod.rs +43 -0
  33. package/src/utils.rs +90 -0
  34. package/target/cjs/procss.d.ts +22 -0
  35. package/target/cjs/procss.js +217 -0
  36. package/target/cjs/procss_bg.wasm +0 -0
  37. package/target/cjs/procss_bg.wasm.d.ts +12 -0
  38. package/target/esm/procss.d.ts +58 -0
  39. package/target/esm/procss.js +283 -0
  40. package/target/esm/procss_bg.wasm +0 -0
  41. package/target/esm/procss_bg.wasm.d.ts +12 -0
@@ -0,0 +1,59 @@
1
+ // ┌───────────────────────────────────────────────────────────────────────────┐
2
+ // │ │
3
+ // │ ██████╗ ██████╗ ██████╗ Copyright (C) 2022, The Prospective Company │
4
+ // │ ██╔══██╗██╔══██╗██╔═══██╗ │
5
+ // │ ██████╔╝██████╔╝██║ ██║ This file is part of the Procss library, │
6
+ // │ ██╔═══╝ ██╔══██╗██║ ██║ distributed under the terms of the │
7
+ // │ ██║ ██║ ██║╚██████╔╝ Apache License 2.0. The full license can │
8
+ // │ ╚═╝ ╚═╝ ╚═╝ ╚═════╝ be found in the LICENSE file. │
9
+ // │ │
10
+ // └───────────────────────────────────────────────────────────────────────────┘
11
+
12
+ use std::collections::HashMap;
13
+
14
+ use wasm_bindgen::prelude::*;
15
+ use wasm_bindgen::JsValue;
16
+
17
+ /// An implementation of `BuildCss` which owns its data, suitable for use as an
18
+ /// exported type in JavaScript.
19
+ #[wasm_bindgen]
20
+ pub struct BuildCss {
21
+ rootdir: String,
22
+ inputs: HashMap<String, String>,
23
+ }
24
+
25
+ #[wasm_bindgen]
26
+ impl BuildCss {
27
+ #[wasm_bindgen(constructor)]
28
+ pub fn new(rootdir: String) -> Self {
29
+ BuildCss {
30
+ rootdir,
31
+ inputs: HashMap::default(),
32
+ }
33
+ }
34
+
35
+ pub fn add(&mut self, path: String, content: String) {
36
+ self.inputs.insert(path, content);
37
+ }
38
+
39
+ pub fn compile(&self) -> core::result::Result<JsValue, JsError> {
40
+ let mut build = crate::builder::BuildCss::new(self.rootdir.clone());
41
+ for (k, v) in self.inputs.iter() {
42
+ build.add_content(k, v.clone());
43
+ }
44
+
45
+ Ok(serde_wasm_bindgen::to_value(
46
+ &build.compile().into_jserr()?.as_strings().into_jserr()?,
47
+ )?)
48
+ }
49
+ }
50
+
51
+ trait IntoJsError<T> {
52
+ fn into_jserr(self) -> Result<T, wasm_bindgen::JsError>;
53
+ }
54
+
55
+ impl<T> IntoJsError<T> for Result<T, anyhow::Error> {
56
+ fn into_jserr(self) -> Result<T, wasm_bindgen::JsError> {
57
+ self.map_err(|x| JsError::from(x.root_cause()))
58
+ }
59
+ }
package/src/lib.rs ADDED
@@ -0,0 +1,151 @@
1
+ // ┌───────────────────────────────────────────────────────────────────────────┐
2
+ // │ │
3
+ // │ ██████╗ ██████╗ ██████╗ Copyright (C) 2022, The Prospective Company │
4
+ // │ ██╔══██╗██╔══██╗██╔═══██╗ │
5
+ // │ ██████╔╝██████╔╝██║ ██║ This file is part of the Procss library, │
6
+ // │ ██╔═══╝ ██╔══██╗██║ ██║ distributed under the terms of the │
7
+ // │ ██║ ██║ ██║╚██████╔╝ Apache License 2.0. The full license can │
8
+ // │ ╚═╝ ╚═╝ ╚═╝ ╚═════╝ be found in the LICENSE file. │
9
+ // │ │
10
+ // └───────────────────────────────────────────────────────────────────────────┘
11
+
12
+ //! A simple CSS parsing and transformation framework. Procss can be used to
13
+ //! quickly bundle a collection of CSS+ files, or write your own custom
14
+ //! transforms.
15
+ //!
16
+ //! # Usage
17
+ //!
18
+ //! Procss's parser understands a nested superset of CSS (which we refer to as
19
+ //! CSS+), similar to the [CSS nesting proposal](https://www.w3.org/TR/css-nesting-1/),
20
+ //! or languages like [Sass](https://sass-lang.com). Start with source CSS+
21
+ //! as a [`str`], use [crate::parse] or [crate::parse_unchecked] to generate
22
+ //! an [`ast::Tree`].
23
+ //!
24
+ //! ```
25
+ //! use procss::{ast, parse};
26
+ //!
27
+ //! let ast = procss::parse("div{.open{color:red;}}").unwrap();
28
+ //! ```
29
+ //!
30
+ //! The resulting [`ast::Tree`] can be converted to a de-nested [`ast::Css`]
31
+ //! with the [`ast::Tree::flatten_tree`] method, which itself can then be
32
+ //! rendered as a plain browser-readable CSS string via the
33
+ //! [`RenderCss::as_css_string`] trait method.
34
+ //!
35
+ //! ```
36
+ //! # use procss::{parse, ast};
37
+ //! # let ast = procss::parse("div{.open{color:red;}}").unwrap();
38
+ //! use procss::RenderCss;
39
+ //!
40
+ //! let flat: ast::Css = ast.flatten_tree();
41
+ //! let css: String = flat.as_css_string();
42
+ //! assert_eq!(css, "div .open{color:red;}");
43
+ //! ```
44
+ //!
45
+ //! Intermediate structs [`ast::Css::transform`] amd [`ast::Tree::transform`]
46
+ //! can be used to recursively mutate a tree for a variety of node structs in
47
+ //! the [`ast`] module. Some useful Example of such transforms can be
48
+ //! found in the [`transformers`] module.
49
+ //!
50
+ //! ```
51
+ //! # use procss::{parse, RenderCss};
52
+ //! use procss::transformers;
53
+ //!
54
+ //! let test = "
55
+ //! @mixin test {color: red;}
56
+ //! div {@include test;}
57
+ //! ";
58
+ //!
59
+ //! let mut ast = procss::parse(test).unwrap();
60
+ //! transformers::apply_mixin(&mut ast);
61
+ //! let flat = ast.flatten_tree().as_css_string();
62
+ //! assert_eq!(flat, "div{color:red;}");
63
+ //! ```
64
+ //!
65
+ //! For coordinating large builds on a tree of CSS files, the [`BuildCss`]
66
+ //! struct can parse and minify, applying all transforms (including
67
+ //! [`transformers::apply_import`]) as the compilation is left-folded over the
68
+ //! inputs.
69
+ //!
70
+ //! ```no_run
71
+ //! let mut build = procss::BuildCss::new("./src");
72
+ //! build.add_file("controls/menu.scss");
73
+ //! build.add_file("logout.scss"); // imports "controls/menu.scss"
74
+ //! build.add_file("my_app.scss"); // imports "controls/menu.scss" and "logout.scss"
75
+ //! build.compile().unwrap().write("./dist").unwrap();
76
+ //! ```
77
+
78
+ #![feature(assert_matches)]
79
+ #![feature(path_file_prefix)]
80
+
81
+ pub mod ast;
82
+ mod builder;
83
+ #[cfg(target_arch = "wasm32")]
84
+ mod js_builder;
85
+ mod parser;
86
+ mod render;
87
+ mod transform;
88
+ pub mod transformers;
89
+ mod utils;
90
+
91
+ use self::ast::Tree;
92
+ pub use self::builder::BuildCss;
93
+ use self::parser::{unwrap_parse_error, ParseCss};
94
+ pub use self::render::RenderCss;
95
+
96
+ /// Parse CSS text to a [`Tree`] (where it can be further manipulated),
97
+ /// capturing detailed error reporting for a moderate performance impact (using
98
+ /// [`nom::error::VerboseError`]).
99
+ ///
100
+ /// # Example
101
+ ///
102
+ /// ```rust
103
+ /// let ast = procss::parse("div { .open { color: red; }}").unwrap();
104
+ /// ```
105
+ pub fn parse(input: &str) -> anyhow::Result<Tree<'_>> {
106
+ let (_, tree) = Tree::parse(input).map_err(|err| unwrap_parse_error(input, err))?;
107
+ Ok(tree)
108
+ }
109
+
110
+ /// Parse CSS text to a [`Tree`], without capturing error details, for maximum
111
+ /// performance without any error details when parsing fails.
112
+ ///
113
+ /// # Example
114
+ ///
115
+ /// ```rust
116
+ /// let ast = procss::parse_unchecked("div { .open { color: red; }}").unwrap();
117
+ /// ```
118
+ pub fn parse_unchecked(input: &str) -> anyhow::Result<Tree<'_>> {
119
+ let (_, tree) = Tree::parse::<()>(input)?;
120
+ Ok(tree)
121
+ }
122
+
123
+ #[cfg(test)]
124
+ mod tests {
125
+ use std::assert_matches::assert_matches;
126
+
127
+ use super::*;
128
+
129
+ #[test]
130
+ fn test_verbose_error() {
131
+ assert_matches!(
132
+ parse("div{color:red").map(|x| x.as_css_string()).as_deref(),
133
+ Err(_)
134
+ )
135
+ }
136
+
137
+ #[test]
138
+ fn test_parse_unchecked() {
139
+ assert_matches!(
140
+ parse_unchecked("div{color:red}")
141
+ .map(|x| x.as_css_string())
142
+ .as_deref(),
143
+ Ok("div{color:red;}")
144
+ )
145
+ }
146
+ }
147
+
148
+ // `iotest` feature flag stubs out disk-accessing and other performance
149
+ // neutering function
150
+ #[cfg(all(not(feature = "iotest"), test))]
151
+ compile_error!("Feature 'iotest' must be enabled, rerun with:\n\n> cargo xtest");
package/src/main.rs ADDED
@@ -0,0 +1,40 @@
1
+ // ┌───────────────────────────────────────────────────────────────────────────┐
2
+ // │ │
3
+ // │ ██████╗ ██████╗ ██████╗ Copyright (C) 2022, The Prospective Company │
4
+ // │ ██╔══██╗██╔══██╗██╔═══██╗ │
5
+ // │ ██████╔╝██████╔╝██║ ██║ This file is part of the Procss library, │
6
+ // │ ██╔═══╝ ██╔══██╗██║ ██║ distributed under the terms of the │
7
+ // │ ██║ ██║ ██║╚██████╔╝ Apache License 2.0. The full license can │
8
+ // │ ╚═╝ ╚═╝ ╚═╝ ╚═════╝ be found in the LICENSE file. │
9
+ // │ │
10
+ // └───────────────────────────────────────────────────────────────────────────┘
11
+
12
+ /// This needs to be here or the wasm build will not export any library symbols.
13
+ #[allow(unused_imports)]
14
+ use procss::*;
15
+
16
+ #[cfg(not(target_arch = "wasm32"))]
17
+ mod init {
18
+ use std::path::Path;
19
+ use std::{env, fs};
20
+
21
+ use procss::*;
22
+
23
+ pub fn init() -> anyhow::Result<String> {
24
+ let args: Vec<String> = env::args().collect();
25
+ let contents = fs::read_to_string(Path::new(&args[1]));
26
+ let css = parse(&contents?)?.flatten_tree().as_css_string();
27
+ Ok(css)
28
+ }
29
+ }
30
+
31
+ #[cfg(not(target_arch = "wasm32"))]
32
+ fn main() {
33
+ match init::init() {
34
+ Ok(x) => println!("{}", x),
35
+ Err(x) => eprintln!("{}", x),
36
+ }
37
+ }
38
+
39
+ #[cfg(target_arch = "wasm32")]
40
+ fn main() {}
package/src/parser.rs ADDED
@@ -0,0 +1,36 @@
1
+ // ┌───────────────────────────────────────────────────────────────────────────┐
2
+ // │ │
3
+ // │ ██████╗ ██████╗ ██████╗ Copyright (C) 2022, The Prospective Company │
4
+ // │ ██╔══██╗██╔══██╗██╔═══██╗ │
5
+ // │ ██████╔╝██████╔╝██║ ██║ This file is part of the Procss library, │
6
+ // │ ██╔═══╝ ██╔══██╗██║ ██║ distributed under the terms of the │
7
+ // │ ██║ ██║ ██║╚██████╔╝ Apache License 2.0. The full license can │
8
+ // │ ╚═╝ ╚═╝ ╚═╝ ╚═════╝ be found in the LICENSE file. │
9
+ // │ │
10
+ // └───────────────────────────────────────────────────────────────────────────┘
11
+
12
+ use anyhow::anyhow;
13
+ use nom::error::{convert_error, ParseError, VerboseError};
14
+ use nom::{Err, IResult};
15
+
16
+ /// A trait for CSS AST types which can be parsed from a String.
17
+ pub trait ParseCss<'a>
18
+ where
19
+ Self: Sized,
20
+ {
21
+ /// Parse an input string into the trait implementor, parameterized by an
22
+ /// invoker-chosen `E` error type which allows compile-time choice between
23
+ /// fast or debug parser implementations.
24
+ fn parse<E>(input: &'a str) -> IResult<&'a str, Self, E>
25
+ where
26
+ E: ParseError<&'a str>;
27
+ }
28
+
29
+ pub fn unwrap_parse_error(input: &str, err: Err<VerboseError<&str>>) -> anyhow::Error {
30
+ match err {
31
+ Err::Error(e) | Err::Failure(e) => {
32
+ anyhow!("Error parsing, unknown:\n{}", convert_error(input, e))
33
+ }
34
+ Err::Incomplete(needed) => anyhow!("Error parsing, unexpected input:\n {:?}", needed),
35
+ }
36
+ }
package/src/render.rs ADDED
@@ -0,0 +1,84 @@
1
+ // ┌───────────────────────────────────────────────────────────────────────────┐
2
+ // │ │
3
+ // │ ██████╗ ██████╗ ██████╗ Copyright (C) 2022, The Prospective Company │
4
+ // │ ██╔══██╗██╔══██╗██╔═══██╗ │
5
+ // │ ██████╔╝██████╔╝██║ ██║ This file is part of the Procss library, │
6
+ // │ ██╔═══╝ ██╔══██╗██║ ██║ distributed under the terms of the │
7
+ // │ ██║ ██║ ██║╚██████╔╝ Apache License 2.0. The full license can │
8
+ // │ ╚═╝ ╚═╝ ╚═╝ ╚═════╝ be found in the LICENSE file. │
9
+ // │ │
10
+ // └───────────────────────────────────────────────────────────────────────────┘
11
+
12
+ use std::borrow::Cow;
13
+ use std::fmt;
14
+
15
+ /// A trick to etract the top-level `Formatter` when rendering to a string.
16
+ struct Fix<F: Fn(&mut fmt::Formatter<'_>)>(F);
17
+
18
+ impl<F: Fn(&mut fmt::Formatter<'_>)> fmt::Display for Fix<F> {
19
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
20
+ (self.0)(f);
21
+ Ok(())
22
+ }
23
+ }
24
+
25
+ // Another form of [`std::fmt::Display`] etc. This one is explicitly for
26
+ // generating valid CSS as a string.
27
+ pub trait RenderCss {
28
+ fn render(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result;
29
+
30
+ /// Render `self` as a valid CSS [`String`], minified (with
31
+ /// non-interpretation-impacting whitespace removed).
32
+ fn as_css_string(&self) -> String {
33
+ format!(
34
+ "{}",
35
+ Fix(|fmt| {
36
+ self.render(fmt).unwrap();
37
+ })
38
+ )
39
+ }
40
+ }
41
+
42
+ impl<'a> RenderCss for &'a str {
43
+ fn render(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
44
+ write!(f, "{}", self)
45
+ }
46
+ }
47
+
48
+ impl<'a, T: RenderCss + Clone> RenderCss for Cow<'a, T> {
49
+ fn render(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
50
+ self.as_ref().render(f)
51
+ }
52
+ }
53
+
54
+ impl<T: RenderCss> RenderCss for Option<T> {
55
+ fn render(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
56
+ match self {
57
+ Some(x) => x.render(f),
58
+ None => Ok(()),
59
+ }
60
+ }
61
+ }
62
+
63
+ impl RenderCss for () {
64
+ fn render(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
65
+ Ok(())
66
+ }
67
+ }
68
+
69
+ impl<T: RenderCss> RenderCss for Vec<T> {
70
+ fn render(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
71
+ for x in self {
72
+ x.render(f)?;
73
+ }
74
+
75
+ Ok(())
76
+ }
77
+ }
78
+
79
+ impl<T: RenderCss, U: RenderCss> RenderCss for (T, U) {
80
+ fn render(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
81
+ self.0.render(f)?;
82
+ self.1.render(f)
83
+ }
84
+ }
@@ -0,0 +1,14 @@
1
+ // ┌───────────────────────────────────────────────────────────────────────────┐
2
+ // │ │
3
+ // │ ██████╗ ██████╗ ██████╗ Copyright (C) 2022, The Prospective Company │
4
+ // │ ██╔══██╗██╔══██╗██╔═══██╗ │
5
+ // │ ██████╔╝██████╔╝██║ ██║ This file is part of the Procss library, │
6
+ // │ ██╔═══╝ ██╔══██╗██║ ██║ distributed under the terms of the │
7
+ // │ ██║ ██║ ██║╚██████╔╝ Apache License 2.0. The full license can │
8
+ // │ ╚═╝ ╚═╝ ╚═╝ ╚═════╝ be found in the LICENSE file. │
9
+ // │ │
10
+ // └───────────────────────────────────────────────────────────────────────────┘
11
+
12
+ pub trait TransformCss<T> {
13
+ fn transform_each<F: FnMut(&mut T)>(&mut self, f: &mut F);
14
+ }
@@ -0,0 +1,53 @@
1
+ // ┌───────────────────────────────────────────────────────────────────────────┐
2
+ // │ │
3
+ // │ ██████╗ ██████╗ ██████╗ Copyright (C) 2022, The Prospective Company │
4
+ // │ ██╔══██╗██╔══██╗██╔═══██╗ │
5
+ // │ ██████╔╝██████╔╝██║ ██║ This file is part of the Procss library, │
6
+ // │ ██╔═══╝ ██╔══██╗██║ ██║ distributed under the terms of the │
7
+ // │ ██║ ██║ ██║╚██████╔╝ Apache License 2.0. The full license can │
8
+ // │ ╚═╝ ╚═╝ ╚═╝ ╚═════╝ be found in the LICENSE file. │
9
+ // │ │
10
+ // └───────────────────────────────────────────────────────────────────────────┘
11
+
12
+ use std::collections::HashMap;
13
+
14
+ use super::filter_refs;
15
+ use crate::ast::Ruleset::{self};
16
+ use crate::ast::*;
17
+
18
+ pub fn apply_import<'a, 'b>(assets: &'b HashMap<&str, Tree<'a>>) -> impl Fn(&mut Tree<'a>) + 'b {
19
+ |tree| {
20
+ tree.transform(|ruleset| {
21
+ let mut replace = None;
22
+ if let Ruleset::QualRule(QualRule(name, val)) = ruleset {
23
+ if *name == "import" {
24
+ if let Some(val) = val {
25
+ if val.starts_with('\"') {
26
+ replace = assets.get(&val[1..val.len() - 1]).cloned();
27
+ if replace.is_none() {
28
+ panic!("File not found: '{}'", &val[1..val.len() - 1])
29
+ }
30
+ } else if val.starts_with("url(\"ref://") {
31
+ replace = assets.get(&val[11..val.len() - 2]).cloned().map(|mut x| {
32
+ filter_refs(&mut x);
33
+ x
34
+ });
35
+
36
+ if replace.is_none() {
37
+ panic!("File not found: '{}'", &val[1..val.len() - 1])
38
+ }
39
+ }
40
+ }
41
+ }
42
+ }
43
+
44
+ if let Some(replace) = replace {
45
+ let rules: Vec<TreeRule<'a>> =
46
+ replace.iter().cloned().map(TreeRule::Ruleset).collect();
47
+
48
+ *ruleset =
49
+ Ruleset::SelectorRuleset(SelectorRuleset(Selector::default(), rules.clone()))
50
+ }
51
+ });
52
+ }
53
+ }
@@ -0,0 +1,73 @@
1
+ // ┌───────────────────────────────────────────────────────────────────────────┐
2
+ // │ │
3
+ // │ ██████╗ ██████╗ ██████╗ Copyright (C) 2022, The Prospective Company │
4
+ // │ ██╔══██╗██╔══██╗██╔═══██╗ │
5
+ // │ ██████╔╝██████╔╝██║ ██║ This file is part of the Procss library, │
6
+ // │ ██╔═══╝ ██╔══██╗██║ ██║ distributed under the terms of the │
7
+ // │ ██║ ██║ ██║╚██████╔╝ Apache License 2.0. The full license can │
8
+ // │ ╚═╝ ╚═╝ ╚═╝ ╚═════╝ be found in the LICENSE file. │
9
+ // │ │
10
+ // └───────────────────────────────────────────────────────────────────────────┘
11
+
12
+ use std::collections::HashMap;
13
+
14
+ use crate::ast::Ruleset::{self};
15
+ use crate::ast::*;
16
+
17
+ /// Apply any in-scope mixin (defined using `@mixin`) to any `@include` in the
18
+ /// [`Tree`] `input`. Similar to [Sass `@mixin`](https://sass-lang.com/documentation/at-rules/mixin)
19
+ ///
20
+ /// # Example
21
+ ///
22
+ /// ```
23
+ /// # use procss::{parse, transformers::apply_mixin, RenderCss};
24
+ /// let css = "
25
+ /// @mixin test {
26
+ /// opacity: 0;
27
+ /// }
28
+ /// div.open {
29
+ /// color: red;
30
+ /// @include test;
31
+ /// }
32
+ /// ";
33
+ /// let mut tree = parse(css).unwrap();
34
+ /// apply_mixin(&mut tree);
35
+ /// let css = tree.flatten_tree().as_css_string();
36
+ /// assert_eq!(css, "div.open{color:red;}div.open{opacity:0;}");
37
+ /// ```
38
+ pub fn apply_mixin<'a>(tree: &mut Tree<'a>) {
39
+ let mut mixins: HashMap<&'a str, Vec<TreeRule<'a>>> = HashMap::new();
40
+ tree.transform(|ruleset| {
41
+ if let Ruleset::QualRuleset(crate::ast::QualRuleset(QualRule(name, Some(val)), props)) = ruleset {
42
+ if *name == "mixin" {
43
+ mixins.insert(val.trim(), props.clone());
44
+ }
45
+ }
46
+
47
+ if matches!(ruleset, Ruleset::QualRuleset(QualRuleset(QualRule(name, _), _)) if *name == "mixin") {
48
+ *ruleset = Ruleset::SelectorRuleset(SelectorRuleset(Selector::default(), vec![]))
49
+ }
50
+ });
51
+
52
+ let mut count = 5;
53
+ while count > 0 {
54
+ let mut changed = false;
55
+ tree.transform(|ruleset| {
56
+ for (header, props) in mixins.iter() {
57
+ if matches!(ruleset, Ruleset::QualRule(QualRule("include", Some(val))) if val == header ) {
58
+ changed = true;
59
+ *ruleset = Ruleset::SelectorRuleset(SelectorRuleset(
60
+ Selector::default(),
61
+ props.clone().into_iter().collect(),
62
+ ))
63
+ }
64
+ }
65
+ });
66
+
67
+ if !changed {
68
+ count = 0;
69
+ } else {
70
+ count -= 1;
71
+ }
72
+ }
73
+ }
@@ -0,0 +1,41 @@
1
+ // ┌───────────────────────────────────────────────────────────────────────────┐
2
+ // │ │
3
+ // │ ██████╗ ██████╗ ██████╗ Copyright (C) 2022, The Prospective Company │
4
+ // │ ██╔══██╗██╔══██╗██╔═══██╗ │
5
+ // │ ██████╔╝██████╔╝██║ ██║ This file is part of the Procss library, │
6
+ // │ ██╔═══╝ ██╔══██╗██║ ██║ distributed under the terms of the │
7
+ // │ ██║ ██║ ██║╚██████╔╝ Apache License 2.0. The full license can │
8
+ // │ ╚═╝ ╚═╝ ╚═╝ ╚═════╝ be found in the LICENSE file. │
9
+ // │ │
10
+ // └───────────────────────────────────────────────────────────────────────────┘
11
+
12
+ use std::collections::HashMap;
13
+
14
+ use crate::ast::Ruleset::{self};
15
+ use crate::ast::*;
16
+
17
+ pub fn apply_var<'a>(tree: &mut Tree<'a>) {
18
+ let mut mixins: HashMap<&'a str, &'a str> = HashMap::new();
19
+ tree.transform(|ruleset| {
20
+ let mut is_mixin = false;
21
+ if let Ruleset::QualRule(QualRule(name, Some(val))) = ruleset {
22
+ if let Some(val) = val.strip_prefix(':') {
23
+ mixins.insert(name, val);
24
+ is_mixin = true;
25
+ }
26
+ }
27
+
28
+ if is_mixin {
29
+ *ruleset = Ruleset::QualRuleset(QualRuleset(QualRule("", None), vec![]))
30
+ }
31
+ });
32
+
33
+ let mut mixins = mixins.iter().collect::<Vec<_>>();
34
+ mixins.sort_by(|(x, _), (a, _)| x.len().cmp(&a.len()));
35
+ mixins.reverse();
36
+ tree.transform(|rule: &mut Rule| {
37
+ for (var, val) in mixins.iter() {
38
+ rule.value = rule.value.replace(&format!("@{}", var), val).into();
39
+ }
40
+ });
41
+ }
@@ -0,0 +1,35 @@
1
+ // ┌───────────────────────────────────────────────────────────────────────────┐
2
+ // │ │
3
+ // │ ██████╗ ██████╗ ██████╗ Copyright (C) 2022, The Prospective Company │
4
+ // │ ██╔══██╗██╔══██╗██╔═══██╗ │
5
+ // │ ██████╔╝██████╔╝██║ ██║ This file is part of the Procss library, │
6
+ // │ ██╔═══╝ ██╔══██╗██║ ██║ distributed under the terms of the │
7
+ // │ ██║ ██║ ██║╚██████╔╝ Apache License 2.0. The full license can │
8
+ // │ ╚═╝ ╚═╝ ╚═╝ ╚═════╝ be found in the LICENSE file. │
9
+ // │ │
10
+ // └───────────────────────────────────────────────────────────────────────────┘
11
+
12
+ use crate::ast::Ruleset::{self};
13
+ use crate::ast::*;
14
+
15
+ pub fn dedupe(css: &mut Css) {
16
+ let mut res = vec![];
17
+ let reduced = css.iter().cloned().reduce(|x, y| match (x, y) {
18
+ (Ruleset::QualRule(x), Ruleset::QualRule(y)) if x == y => Ruleset::QualRule(x),
19
+ (Ruleset::SelectorRuleset(x), Ruleset::SelectorRuleset(y)) if x.0 == y.0 => {
20
+ let mut tail = x.1.clone();
21
+ tail.extend(y.1);
22
+ Ruleset::SelectorRuleset(SelectorRuleset(x.0.clone(), tail))
23
+ }
24
+ x => {
25
+ res.push(x.0);
26
+ x.1
27
+ }
28
+ });
29
+
30
+ if let Some(reduced) = reduced {
31
+ res.push(reduced.clone());
32
+ }
33
+
34
+ *css = crate::ast::Css(res)
35
+ }
@@ -0,0 +1,27 @@
1
+ // ┌───────────────────────────────────────────────────────────────────────────┐
2
+ // │ │
3
+ // │ ██████╗ ██████╗ ██████╗ Copyright (C) 2022, The Prospective Company │
4
+ // │ ██╔══██╗██╔══██╗██╔═══██╗ │
5
+ // │ ██████╔╝██████╔╝██║ ██║ This file is part of the Procss library, │
6
+ // │ ██╔═══╝ ██╔══██╗██║ ██║ distributed under the terms of the │
7
+ // │ ██║ ██║ ██║╚██████╔╝ Apache License 2.0. The full license can │
8
+ // │ ╚═╝ ╚═╝ ╚═╝ ╚═════╝ be found in the LICENSE file. │
9
+ // │ │
10
+ // └───────────────────────────────────────────────────────────────────────────┘
11
+
12
+ use crate::ast::Ruleset::{self};
13
+ use crate::ast::*;
14
+
15
+ pub fn filter_refs(tree: &mut Tree) {
16
+ *tree = Tree(
17
+ tree.iter()
18
+ .cloned()
19
+ .filter(|y| match y {
20
+ Ruleset::SelectorRuleset(_) => false,
21
+ Ruleset::QualRule(_) => true,
22
+ Ruleset::QualRuleset(_) => true,
23
+ Ruleset::QualNestedRuleset(_) => true,
24
+ })
25
+ .collect(),
26
+ )
27
+ }
@@ -0,0 +1,30 @@
1
+ // ┌───────────────────────────────────────────────────────────────────────────┐
2
+ // │ │
3
+ // │ ██████╗ ██████╗ ██████╗ Copyright (C) 2022, The Prospective Company │
4
+ // │ ██╔══██╗██╔══██╗██╔═══██╗ │
5
+ // │ ██████╔╝██████╔╝██║ ██║ This file is part of the Procss library, │
6
+ // │ ██╔═══╝ ██╔══██╗██║ ██║ distributed under the terms of the │
7
+ // │ ██║ ██║ ██║╚██████╔╝ Apache License 2.0. The full license can │
8
+ // │ ╚═╝ ╚═╝ ╚═╝ ╚═════╝ be found in the LICENSE file. │
9
+ // │ │
10
+ // └───────────────────────────────────────────────────────────────────────────┘
11
+
12
+ use crate::ast::{Css, SelectorPath};
13
+ use crate::transform::*;
14
+
15
+ /// Remove `&` references from a flattened `Css`.
16
+ pub(crate) fn flat_self(css: &mut Css) {
17
+ css.transform_each(&mut |x: &mut SelectorPath| {
18
+ let res = match x {
19
+ SelectorPath::Cons(..) => None,
20
+ SelectorPath::PartialCons(_, tail) if !tail.is_empty() => {
21
+ Some(SelectorPath::Cons(tail.remove(0).1, tail.clone()))
22
+ }
23
+ _ => None,
24
+ };
25
+
26
+ if let Some(y) = res {
27
+ *x = y;
28
+ }
29
+ });
30
+ }