swc_css_parser/parser/
mod.rs

1use std::mem::take;
2
3use serde::{Deserialize, Serialize};
4use swc_css_ast::*;
5
6use self::input::{Buffer, ParserInput};
7use crate::{error::Error, Parse};
8
9#[macro_use]
10mod macros;
11mod at_rules;
12pub mod input;
13mod selectors;
14mod syntax;
15#[cfg(test)]
16mod tests;
17mod util;
18mod values_and_units;
19
20pub type PResult<T> = Result<T, Error>;
21
22#[derive(
23    Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize,
24)]
25#[serde(rename_all = "camelCase")]
26pub struct ParserConfig {
27    /// If this is `true`, **wrong** comments starting with `//` will be treated
28    /// as a comment.
29    ///
30    /// This option exists because there are so many css-in-js tools and people
31    /// use `//` as a comment because it's javascript file.
32    ///
33    /// Defaults to `false`.
34    #[serde(default)]
35    pub allow_wrong_line_comments: bool,
36
37    /// If enabled, errors for css modules selectors will be ignored.
38    ///
39    ///
40    /// Defaults to `false`.
41    #[serde(default)]
42    pub css_modules: bool,
43
44    /// If this is `true`, the nested selector starts with an identifier will be
45    /// parsed as valid selectors (i.e. `ul { color: red; li { color: blue } }`)
46    ///
47    /// Defaults to `false`.
48    #[serde(default)]
49    pub legacy_nesting: bool,
50
51    /// If this is `true`, the legacy syntax for IE will be parsed.
52    #[serde(default)]
53    pub legacy_ie: bool,
54}
55
56#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
57pub enum BlockContentsGrammar {
58    StyleBlock,
59    DeclarationList,
60    RuleList,
61    #[default]
62    Stylesheet,
63}
64
65#[derive(Debug, Clone, Copy)]
66struct Ctx {
67    is_top_level: bool,
68    block_contents_grammar: BlockContentsGrammar,
69    mixed_with_declarations: bool,
70    need_canonicalize: bool,
71
72    in_keyframes_at_rule: bool,
73    in_supports_at_rule: bool,
74    in_import_at_rule: bool,
75    in_page_at_rule: bool,
76    in_container_at_rule: bool,
77    in_font_feature_values_at_rule: bool,
78
79    in_global_or_local_selector: bool,
80}
81
82impl Default for Ctx {
83    fn default() -> Self {
84        Ctx {
85            is_top_level: false,
86            block_contents_grammar: BlockContentsGrammar::default(),
87            mixed_with_declarations: false,
88            need_canonicalize: true,
89
90            in_keyframes_at_rule: false,
91            in_supports_at_rule: false,
92            in_import_at_rule: false,
93            in_page_at_rule: false,
94            in_container_at_rule: false,
95            in_font_feature_values_at_rule: false,
96            in_global_or_local_selector: false,
97        }
98    }
99}
100
101#[derive(Debug, Clone)]
102pub struct Parser<I>
103where
104    I: ParserInput,
105{
106    #[allow(dead_code)]
107    config: ParserConfig,
108    input: Buffer<I>,
109    ctx: Ctx,
110    errors: Vec<Error>,
111}
112
113impl<I> Parser<I>
114where
115    I: ParserInput,
116{
117    pub fn new(input: I, config: ParserConfig) -> Self {
118        Parser {
119            config,
120            input: Buffer::new(input),
121            ctx: Default::default(),
122            errors: Default::default(),
123        }
124    }
125
126    pub fn dump_cur(&mut self) -> String {
127        format!("{:?}", self.input.cur())
128    }
129
130    /// Take **recovered** errors.
131    pub fn take_errors(&mut self) -> Vec<Error> {
132        take(&mut self.errors)
133    }
134
135    pub fn parse_all(&mut self) -> PResult<Stylesheet> {
136        self.parse()
137    }
138}