weavatrix_parse/style/mod.rs
1//! Structural extraction for CSS, SCSS, Sass and Less.
2//!
3//! A stylesheet is a reference graph rather than a call graph: what matters is
4//! which selectors a file declares, because that is what an HTML `class` or
5//! `id` attribute resolves to, and which other stylesheets it pulls in.
6//!
7//! Selectors are read from the token stream rather than by matching lines,
8//! which is what makes nesting work. In SCSS a rule written inside another
9//! rule is a real selector, and a `&` prefix concatenates it onto its parent -
10//! so `.card { &__title { } }` declares `.card__title`, a name that appears
11//! nowhere in the source as written.
12
13use crate::facts::{Declaration, DeclarationKind, Facts, Import, Reference, ReferenceKind, Span};
14use crate::syntax::Language;
15use crate::token::{Mode, Token, TokenKind, Tokenizer};
16
17/// Extracts structural facts from one stylesheet.
18#[must_use]
19pub fn extract(source: &str, language: Language) -> Facts {
20 let tokens = Tokenizer::new(source, language)
21 .mode(Mode::Lite)
22 .collect::<Vec<_>>();
23 let mut state = Extractor {
24 source,
25 tokens: &tokens,
26 facts: Facts::default(),
27 nesting: Vec::new(),
28 };
29 state.run();
30 state.facts
31}
32
33/// At-rules that name another stylesheet.
34const AT_IMPORTS: &[&str] = &["import", "use", "forward"];
35
36struct Extractor<'source, 'tokens> {
37 source: &'source str,
38 tokens: &'tokens [Token],
39 facts: Facts,
40 /// Selector prefixes of the enclosing rules, one per open brace.
41 nesting: Vec<String>,
42}
43
44/// Records that a document uses a selector, which is what an HTML `class` or
45/// `id` attribute does. Kept here so the HTML extractor and this one agree on
46/// how a selector is named.
47pub(crate) fn selector_use(facts: &mut Facts, name: String, span: Span) {
48 facts.references.push(Reference {
49 name,
50 kind: ReferenceKind::Uses,
51 receiver: None,
52 span,
53 owner: None,
54 string_arguments: Vec::new(),
55 name_arguments: Vec::new(),
56 });
57}
58
59mod extractor;
60
61#[cfg(test)]
62mod tests;