Skip to main content

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;