Skip to main content

oxc_css_parser/parser/at_rule/
import.rs

1use super::Parser;
2use crate::{
3    Parse, Syntax,
4    ast::*,
5    bump,
6    error::{Error, ErrorKind, PResult},
7    expect, expect_without_ws_or_comments, peek,
8    pos::{Span, Spanned},
9    tokenizer::{Token, TokenWithSpan},
10};
11
12// https://www.w3.org/TR/css-cascade-5/#at-import
13impl<'cmt, 's: 'cmt> Parse<'cmt, 's> for ImportPrelude<'s> {
14    fn parse(input: &mut Parser<'cmt, 's>) -> PResult<Self> {
15        let href = match &peek!(input).token {
16            Token::Str(..) | Token::StrTemplate(..) => input.parse().map(ImportPreludeHref::Str)?,
17            _ => match input.try_parse(Url::parse) {
18                Ok(url) => ImportPreludeHref::Url(url),
19                // Sass only: the content of `url(...)` may be SassScript that
20                // is not a parsable URL, e.g. `@import url($dir+"/path");`.
21                // Mirrors the fallback in `parse_component_value_atom`.
22                Err(error) if matches!(input.syntax, Syntax::Scss | Syntax::Sass) => {
23                    let function_name: Ident = expect!(input, Ident).into();
24                    if !function_name.name.eq_ignore_ascii_case("url") {
25                        return Err(error);
26                    }
27                    input
28                        .parse_function(InterpolableIdent::Literal(function_name))
29                        .map(ImportPreludeHref::Function)
30                        .map_err(|_| error)?
31                }
32                Err(error) => return Err(error),
33            },
34        };
35        let mut span = href.span().clone();
36
37        let layer = match &peek!(input).token {
38            Token::Ident(ident) if ident.name().eq_ignore_ascii_case("layer") => {
39                let ident = input.parse::<Ident>()?;
40                let layer = match peek!(input) {
41                    TokenWithSpan {
42                        token: Token::LParen(..),
43                        span,
44                    } if span.start == ident.span.end => {
45                        bump!(input);
46                        let args = vec![input.parse().map(ComponentValue::LayerName)?];
47                        let end = expect!(input, RParen).1.end;
48                        let span = Span {
49                            start: ident.span.start,
50                            end,
51                        };
52                        ImportPreludeLayer::WithName(Function {
53                            name: FunctionName::Ident(InterpolableIdent::Literal(ident)),
54                            args,
55                            span,
56                        })
57                    }
58                    _ => ImportPreludeLayer::Empty(ident),
59                };
60                span.end = layer.span().end;
61                Some(layer)
62            }
63            _ => None,
64        };
65
66        let supports = input.try_parse(|parser| {
67            let (ident, span) = expect!(parser, Ident);
68            if !ident.name().eq_ignore_ascii_case("supports") {
69                return Err(Error {
70                    kind: ErrorKind::TryParseError,
71                    span,
72                });
73            }
74
75            expect_without_ws_or_comments!(parser, LParen);
76
77            let kind = if let Ok(supports_condition) = parser.try_parse(SupportsCondition::parse) {
78                ImportPreludeSupportsKind::SupportsCondition(supports_condition)
79            } else {
80                parser.parse().map(ImportPreludeSupportsKind::Declaration)?
81            };
82            let (_, Span { end, .. }) = expect!(parser, RParen);
83            Ok(ImportPreludeSupports {
84                kind,
85                span: Span {
86                    start: span.start,
87                    end,
88                },
89            })
90        });
91        if let Ok(supports) = &supports {
92            span.end = supports.span().end;
93        }
94
95        let media = if matches!(peek!(input).token, Token::Semicolon(..)) {
96            None
97        } else {
98            let media = input.parse::<MediaQueryList>()?;
99            span.end = media.span.end;
100            Some(media)
101        };
102
103        Ok(ImportPrelude {
104            href,
105            layer,
106            supports: supports.ok(),
107            media,
108            span,
109        })
110    }
111}