Skip to main content

oxc_css_parser/parser/at_rule/
import.rs

1use super::Parser;
2use crate::{
3    Parse, Syntax, arena_vec,
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<'a> Parse<'a> for ImportPrelude<'a> {
14    fn parse(input: &mut Parser<'a>) -> 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, function_name_span) = expect!(input, Ident);
24                    let function_name = input.ident(function_name, function_name_span);
25                    if !function_name.name.eq_ignore_ascii_case("url") {
26                        return Err(error);
27                    }
28                    input
29                        .parse_function(InterpolableIdent::Literal(function_name))
30                        .map(ImportPreludeHref::Function)
31                        .map_err(|_| error)?
32                }
33                Err(error) => return Err(error),
34            },
35        };
36        let mut span = href.span().clone();
37
38        let layer = match &peek!(input).token {
39            Token::Ident(ident) if ident.name().eq_ignore_ascii_case("layer") => {
40                let ident = input.parse::<Ident>()?;
41                let layer = match peek!(input) {
42                    TokenWithSpan { token: Token::LParen(..), span }
43                        if span.start == ident.span.end =>
44                    {
45                        bump!(input);
46                        let args = arena_vec!(input; input.parse().map(ComponentValue::LayerName)?);
47                        let end = expect!(input, RParen).1.end;
48                        let span = Span { start: ident.span.start, end };
49                        ImportPreludeLayer::WithName(Function {
50                            name: FunctionName::Ident(InterpolableIdent::Literal(ident)),
51                            args,
52                            span,
53                        })
54                    }
55                    _ => ImportPreludeLayer::Empty(ident),
56                };
57                span.end = layer.span().end;
58                Some(layer)
59            }
60            _ => None,
61        };
62
63        let supports = input.try_parse(|parser| {
64            let (ident, span) = expect!(parser, Ident);
65            if !ident.name().eq_ignore_ascii_case("supports") {
66                return Err(Error { kind: ErrorKind::TryParseError, span });
67            }
68
69            expect_without_ws_or_comments!(parser, LParen);
70
71            let kind = if let Ok(supports_condition) = parser.try_parse(SupportsCondition::parse) {
72                ImportPreludeSupportsKind::SupportsCondition(supports_condition)
73            } else {
74                parser.parse().map(ImportPreludeSupportsKind::Declaration)?
75            };
76            let (_, Span { end, .. }) = expect!(parser, RParen);
77            Ok(ImportPreludeSupports { kind, span: Span { start: span.start, end } })
78        });
79        if let Ok(supports) = &supports {
80            span.end = supports.span().end;
81        }
82
83        let media = if matches!(peek!(input).token, Token::Semicolon(..)) {
84            None
85        } else {
86            let media = input.parse::<MediaQueryList>()?;
87            span.end = media.span.end;
88            Some(media)
89        };
90
91        Ok(ImportPrelude { href, layer, supports: supports.ok(), media, span })
92    }
93}