@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,90 @@
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 nom::bytes::complete::{is_not, tag};
13
+ use nom::combinator::opt;
14
+ use nom::error::ParseError;
15
+ use nom::sequence::{preceded, tuple};
16
+ use nom::IResult;
17
+
18
+ use crate::ast::token::*;
19
+ use crate::parser::*;
20
+
21
+ /// A selector which matches attributes, optionally against their value as well.
22
+ /// TODO doesn't support comma-separated multiple selectors.
23
+ ///
24
+ /// # Example
25
+ ///
26
+ /// ```css
27
+ /// div[name=test] {}
28
+ /// div[disabled,data-value="red"] {}
29
+ /// ```
30
+ #[derive(Clone, Debug, Eq, PartialEq, Hash)]
31
+ pub struct SelectorAttr<'a> {
32
+ pub name: &'a str,
33
+ pub value: Option<&'a str>,
34
+ }
35
+
36
+ impl<'a> ParseCss<'a> for SelectorAttr<'a> {
37
+ fn parse<E>(input: &'a str) -> IResult<&'a str, Self, E>
38
+ where
39
+ E: ParseError<&'a str>,
40
+ {
41
+ let (rest, (_, name, value, _)) = tuple((
42
+ tag("["),
43
+ parse_symbol,
44
+ opt(preceded(tag("="), is_not("]"))),
45
+ tag("]"),
46
+ ))(input)?;
47
+ Ok((rest, SelectorAttr { name, value }))
48
+ }
49
+ }
50
+
51
+ #[cfg(test)]
52
+ mod tests {
53
+ use std::assert_matches::assert_matches;
54
+
55
+ use super::*;
56
+
57
+ #[test]
58
+ fn test_bool() {
59
+ assert_matches!(
60
+ SelectorAttr::parse::<()>("[disabled]"),
61
+ Ok(("", SelectorAttr {
62
+ name: "disabled",
63
+ value: None
64
+ }))
65
+ )
66
+ }
67
+
68
+ #[test]
69
+ fn test_value_quotes() {
70
+ assert_matches!(
71
+ SelectorAttr::parse::<()>("[data-value=\"red\"]"),
72
+ Ok(("", SelectorAttr {
73
+ name: "data-value",
74
+ value: Some("\"red\"")
75
+ }))
76
+ )
77
+ }
78
+
79
+ #[ignore]
80
+ #[test]
81
+ fn test_multiple() {
82
+ assert_matches!(
83
+ SelectorAttr::parse::<()>("[disabled,data-value=\"red\"]"),
84
+ Ok(("", SelectorAttr {
85
+ name: "data-value",
86
+ value: Some("\"red\"")
87
+ }))
88
+ )
89
+ }
90
+ }
@@ -0,0 +1,58 @@
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 nom::branch::alt;
13
+ use nom::bytes::complete::tag;
14
+ use nom::error::ParseError;
15
+ use nom::sequence::delimited;
16
+ use nom::{IResult, Parser};
17
+
18
+ use crate::ast::token::*;
19
+ use crate::parser::*;
20
+ use crate::render::*;
21
+
22
+ /// A selector combinator, used to combine a list of selectors.
23
+ #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
24
+ pub enum Combinator {
25
+ Null,
26
+ Sibling,
27
+ AdjSibling,
28
+ Desc,
29
+ }
30
+
31
+ impl RenderCss for Combinator {
32
+ fn render(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
33
+ match self {
34
+ Combinator::Null => write!(f, " "),
35
+ Combinator::Sibling => write!(f, "~"),
36
+ Combinator::AdjSibling => write!(f, "+"),
37
+ Combinator::Desc => write!(f, ">"),
38
+ }
39
+ }
40
+ }
41
+
42
+ impl<'a> ParseCss<'a> for Combinator {
43
+ fn parse<E>(input: &'a str) -> IResult<&'a str, Self, E>
44
+ where
45
+ E: ParseError<&'a str>,
46
+ {
47
+ delimited(
48
+ comment0,
49
+ alt((
50
+ tag("+").map(|_| Combinator::AdjSibling),
51
+ tag(">").map(|_| Combinator::Desc),
52
+ tag("~").map(|_| Combinator::Sibling),
53
+ comment0.map(|_| Combinator::Null),
54
+ )),
55
+ comment0,
56
+ )(input)
57
+ }
58
+ }
@@ -0,0 +1,115 @@
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
+ mod attribute;
13
+ mod combinator;
14
+ mod selector_path;
15
+ mod selector_term;
16
+
17
+ use std::ops::Deref;
18
+
19
+ use nom::bytes::complete::tag;
20
+ use nom::error::ParseError;
21
+ use nom::multi::many0;
22
+ use nom::sequence::{delimited, preceded};
23
+ use nom::IResult;
24
+
25
+ pub use self::attribute::SelectorAttr;
26
+ pub use self::combinator::Combinator;
27
+ pub use self::selector_path::SelectorPath;
28
+ pub use self::selector_term::SelectorTerm;
29
+ use super::token::comment0;
30
+ use crate::parser::*;
31
+ use crate::transform::TransformCss;
32
+ use crate::utils::*;
33
+
34
+ /// A set of selector alternatives separated by `,`, for example `div, span`.
35
+ #[derive(Clone, Debug, Eq, PartialEq, Hash)]
36
+ pub struct Selector<'a>(MinVec<SelectorPath<'a>, 1>);
37
+
38
+ impl<'a> Default for Selector<'a> {
39
+ fn default() -> Self {
40
+ //Selector::parse::<()>("&").unwrap().1
41
+ Selector(MinVec::new(
42
+ [SelectorPath::PartialCons(
43
+ SelectorTerm {
44
+ tag: (),
45
+ ..SelectorTerm::default()
46
+ },
47
+ vec![],
48
+ )],
49
+ vec![],
50
+ ))
51
+ }
52
+ }
53
+
54
+ impl<'a> Deref for Selector<'a> {
55
+ type Target = MinVec<SelectorPath<'a>, 1>;
56
+
57
+ fn deref(&self) -> &Self::Target {
58
+ &self.0
59
+ }
60
+ }
61
+
62
+ impl<'a> ParseCss<'a> for Selector<'a> {
63
+ fn parse<E>(input: &'a str) -> IResult<&'a str, Self, E>
64
+ where
65
+ E: ParseError<&'a str>,
66
+ {
67
+ let (input, selector) = SelectorPath::parse(input)?;
68
+ let (input, extra) = many0(preceded(
69
+ delimited(comment0, tag(","), comment0),
70
+ SelectorPath::parse,
71
+ ))(input)?;
72
+
73
+ Ok((input, Selector(MinVec::new([selector], extra))))
74
+ }
75
+ }
76
+
77
+ impl<'a> TransformCss<SelectorPath<'a>> for Selector<'a> {
78
+ fn transform_each<F: FnMut(&mut SelectorPath<'a>)>(&mut self, f: &mut F) {
79
+ for list in self.0.iter_mut() {
80
+ f(list)
81
+ }
82
+ }
83
+ }
84
+
85
+ impl<'a> Selector<'a> {
86
+ /// Create a new `SelectorGroup` from an `Iterator`, which will fail if
87
+ /// there aren't enough elements. This should asserted by the caller.
88
+ fn new(mut iter: impl Iterator<Item = SelectorPath<'a>>) -> Option<Self> {
89
+ Some(Selector(MinVec::new([iter.next()?], iter.collect())))
90
+ }
91
+
92
+ /// `SelectorGroup` uses the underlying `join()` method of the
93
+ /// `SelectorList`, combined via the product of the two
94
+ /// `SelectorGroup`'s items. For example:
95
+ ///
96
+ /// ```css
97
+ /// div, span {
98
+ /// .opened, :hover {
99
+ /// color: red;
100
+ /// }
101
+ /// }
102
+ /// ```
103
+ ///
104
+ /// becomes
105
+ ///
106
+ /// ```css
107
+ /// div .opened, div :hover, span .opened, span :hover {
108
+ /// color: red;
109
+ /// }
110
+ /// ```
111
+ pub fn join(&self, other: &Selector<'a>) -> Selector<'a> {
112
+ let iter = self.0.iter().flat_map(|x| other.iter().map(|y| x.join(y)));
113
+ Self::new(iter).unwrap()
114
+ }
115
+ }
@@ -0,0 +1,230 @@
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 nom::branch::alt;
13
+ use nom::error::ParseError;
14
+ use nom::multi::many0;
15
+ use nom::sequence::tuple;
16
+ use nom::IResult;
17
+
18
+ use super::combinator::*;
19
+ use super::selector_term::*;
20
+ use crate::parser::*;
21
+ use crate::render::*;
22
+
23
+ /// A linked-list-like data structure representing CSS selector lists, which are
24
+ /// selectors separated by combinators like `>`, `+` or most commonly just
25
+ /// whitespace. When the first selector in a `SelectorList` is the special
26
+ /// selector `&`, as special case `PartialCons` elimantes `tag` as a field from
27
+ /// this first Selector, preventing the `SelectorList` from being serialized
28
+ /// before being flattened.
29
+ #[derive(Clone, Debug, Eq, PartialEq, Hash)]
30
+ pub enum SelectorPath<'a> {
31
+ Cons(
32
+ SelectorTerm<'a, Option<&'a str>>,
33
+ Vec<(Combinator, SelectorTerm<'a, Option<&'a str>>)>,
34
+ ),
35
+ PartialCons(
36
+ SelectorTerm<'a, ()>,
37
+ Vec<(Combinator, SelectorTerm<'a, Option<&'a str>>)>,
38
+ ),
39
+ }
40
+
41
+ use SelectorPath::*;
42
+
43
+ impl<'a> SelectorPath<'a> {
44
+ /// Utility method for cons-ing a `Selector` to the head of a
45
+ /// `SelectorList`.
46
+ fn cons(
47
+ &self,
48
+ selector: &SelectorTerm<'a, ()>,
49
+ tail: Vec<(Combinator, SelectorTerm<'a, Option<&'a str>>)>,
50
+ ) -> Self {
51
+ match self {
52
+ //Nil => Nil,
53
+ Cons(x, _) => Cons(x.join(selector), tail),
54
+ PartialCons(x, _) => PartialCons(x.join(selector), tail),
55
+ }
56
+ }
57
+
58
+ /// Utility method for accessing the tail of a `SelectorList`.
59
+ fn tail(&self) -> Vec<(Combinator, SelectorTerm<'a, Option<&'a str>>)> {
60
+ match self {
61
+ // Nil => vec![],
62
+ Cons(_, tail) => tail.clone(),
63
+ PartialCons(_, tail) => tail.clone(),
64
+ }
65
+ }
66
+
67
+ /// Append two `SelectorList`, properly merging `&` references along the
68
+ /// way. For example:
69
+ ///
70
+ /// ```css
71
+ /// div {
72
+ /// &.enabled {
73
+ /// color: red;
74
+ /// }
75
+ /// }
76
+ /// ```
77
+ ///
78
+ /// becomes
79
+ ///
80
+ /// ```css
81
+ /// div.enabled {
82
+ /// color: red;
83
+ /// }
84
+ /// ```
85
+ pub fn join(&self, other: &Self) -> Self {
86
+ match (&self, other) {
87
+ (head, Cons(selector, tail)) => {
88
+ let mut new_tail = head.tail();
89
+ new_tail.push((Combinator::Null, selector.clone()));
90
+ new_tail.append(&mut tail.clone());
91
+ head.cons(&SelectorTerm::default(), new_tail)
92
+ }
93
+ (head, PartialCons(selector2, tail2)) => {
94
+ let mut new_tail = head.tail();
95
+ match new_tail.pop() {
96
+ Some((c, last)) => {
97
+ new_tail.push((c, last.join(selector2)));
98
+ new_tail.extend(tail2.iter().cloned());
99
+ head.cons(&SelectorTerm::default(), new_tail)
100
+ }
101
+ None => {
102
+ new_tail.append(&mut tail2.clone());
103
+ head.cons(selector2, new_tail)
104
+ }
105
+ }
106
+ }
107
+ }
108
+ }
109
+ }
110
+
111
+ impl<'a> RenderCss for SelectorPath<'a> {
112
+ fn render(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
113
+ match &self {
114
+ Cons(selector, rest) => {
115
+ selector.render(f)?;
116
+ rest.render(f)?;
117
+ }
118
+ PartialCons(selector, rest) => {
119
+ write!(f, "&")?;
120
+ selector.render(f)?;
121
+ rest.render(f)?;
122
+ }
123
+ };
124
+
125
+ Ok(())
126
+ }
127
+ }
128
+
129
+ impl<'a> ParseCss<'a> for SelectorPath<'a> {
130
+ fn parse<E>(input: &'a str) -> IResult<&'a str, Self, E>
131
+ where
132
+ E: ParseError<&'a str>,
133
+ {
134
+ alt((parse_selector_self_list, parse_selector_list))(input)
135
+ }
136
+ }
137
+
138
+ /// Parse a selector list without a leading `&` tag, see note on
139
+ /// `parse_selector_self_list`.
140
+ fn parse_selector_list<'a, E>(input: &'a str) -> IResult<&'a str, SelectorPath<'a>, E>
141
+ where
142
+ E: ParseError<&'a str>,
143
+ {
144
+ let (rest, x) = SelectorTerm::parse(input)?;
145
+ let (rest, combinators) = many0(tuple((Combinator::parse, SelectorTerm::parse)))(rest)?;
146
+ Ok((rest, Cons(x, combinators)))
147
+ }
148
+
149
+ /// Parse a selector list starting with the `&` special tag via the
150
+ /// `Selector<()>` impl for the `Parser` trait, which is chosen here by the
151
+ /// compiler due to type inference, from the return binding later used as an
152
+ /// argument to the `PartialCons` variant constructor.
153
+ fn parse_selector_self_list<'a, E>(input: &'a str) -> IResult<&'a str, SelectorPath<'a>, E>
154
+ where
155
+ E: ParseError<&'a str>,
156
+ {
157
+ let (rest, x) = SelectorTerm::parse(input)?;
158
+ let (rest, combinators) = many0(tuple((Combinator::parse, SelectorTerm::parse)))(rest)?;
159
+ Ok((rest, PartialCons(x, combinators)))
160
+ }
161
+
162
+ #[cfg(test)]
163
+ mod tests {
164
+ use std::assert_matches::assert_matches;
165
+
166
+ use super::*;
167
+
168
+ #[test]
169
+ fn test_null() {
170
+ assert_matches!(
171
+ SelectorPath::parse::<()>("div img"),
172
+ Ok((
173
+ "",
174
+ SelectorPath::Cons(
175
+ SelectorTerm {
176
+ tag: Some("div"),
177
+ ..
178
+ },
179
+ xs
180
+ )
181
+ )) if xs.len() == 1 && xs[0].1.tag == Some("img")
182
+ )
183
+ }
184
+
185
+ #[test]
186
+ fn test_desc() {
187
+ assert_matches!(
188
+ SelectorPath::parse::<()>("div > img"),
189
+ Ok((
190
+ "",
191
+ SelectorPath::Cons(
192
+ SelectorTerm {
193
+ tag: Some("div"),
194
+ ..
195
+ },
196
+ xs
197
+ )
198
+ )) if xs.len() == 1 && xs[0].1.tag == Some("img")
199
+ )
200
+ }
201
+
202
+ #[test]
203
+ fn test_self() {
204
+ assert_matches!(
205
+ SelectorPath::parse::<()>("& > img"),
206
+ Ok((
207
+ "",
208
+ SelectorPath::PartialCons(
209
+ _,
210
+ xs
211
+ )
212
+ )) if xs.len() == 1 && xs[0].1.tag == Some("img")
213
+ )
214
+ }
215
+
216
+ #[ignore]
217
+ #[test]
218
+ fn test_inner_self() {
219
+ assert_matches!(
220
+ SelectorPath::parse::<()>("@nest div & img"),
221
+ Ok((
222
+ "",
223
+ SelectorPath::PartialCons(
224
+ _,
225
+ xs
226
+ )
227
+ )) if xs.len() == 1 && xs[0].1.tag == Some("img")
228
+ )
229
+ }
230
+ }