1use crate::properties::custom::Token;
4use crate::rules::Location;
5use crate::values::string::CowArcStr;
6use cssparser::{BasicParseErrorKind, ParseError, ParseErrorKind};
7use parcel_selectors::parser::SelectorParseErrorKind;
8#[cfg(any(feature = "serde", feature = "nodejs"))]
9use serde::Serialize;
10#[cfg(feature = "into_owned")]
11use static_self::IntoOwned;
12use std::fmt;
13
14#[derive(Debug, PartialEq, Clone)]
16#[cfg_attr(any(feature = "serde", feature = "nodejs"), derive(serde::Serialize))]
17#[cfg_attr(any(feature = "serde"), derive(serde::Deserialize))]
18#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
19#[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))]
20pub struct Error<T> {
21 pub kind: T,
23 pub loc: Option<ErrorLocation>,
25}
26
27impl<T: fmt::Display> fmt::Display for Error<T> {
28 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
29 self.kind.fmt(f)?;
30 if let Some(loc) = &self.loc {
31 write!(f, " at {}", loc)?;
32 }
33 Ok(())
34 }
35}
36
37impl<T: fmt::Display + fmt::Debug> std::error::Error for Error<T> {}
38
39#[derive(Debug, PartialEq, Clone)]
41#[cfg_attr(any(feature = "serde", feature = "nodejs"), derive(serde::Serialize))]
42#[cfg_attr(any(feature = "serde"), derive(serde::Deserialize))]
43#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
44#[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))]
45pub struct ErrorLocation {
46 pub filename: String,
48 pub line: u32,
50 pub column: u32,
52}
53
54impl ErrorLocation {
55 pub fn new(loc: Location, filename: String) -> Self {
57 ErrorLocation {
58 filename,
59 line: loc.line,
60 column: loc.column,
61 }
62 }
63}
64
65impl fmt::Display for ErrorLocation {
66 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
67 write!(f, "{}:{}:{}", self.filename, self.line, self.column)
68 }
69}
70
71#[derive(Debug, PartialEq, Clone)]
73#[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))]
74#[cfg_attr(any(feature = "serde", feature = "nodejs"), derive(Serialize))]
75#[cfg_attr(any(feature = "serde", feature = "nodejs"), serde(tag = "type", content = "value"))]
76pub enum ParserError<'i> {
77 AtRuleBodyInvalid,
79 AtRulePreludeInvalid,
81 AtRuleInvalid(CowArcStr<'i>),
83 EndOfInput,
85 InvalidDeclaration,
87 InvalidMediaQuery,
89 EmptyBracketInCondition,
91 InvalidNesting,
93 DeprecatedNestRule,
95 DeprecatedCssModulesValueRule,
97 InvalidPageSelector,
99 InvalidValue,
101 QualifiedRuleInvalid,
103 SelectorError(SelectorError<'i>),
105 UnexpectedImportRule,
107 UnexpectedNamespaceRule,
109 UnexpectedToken(#[cfg_attr(any(feature = "serde", feature = "nodejs"), serde(skip))] Token<'i>),
111 MaximumNestingDepth,
113}
114
115impl<'i> fmt::Display for ParserError<'i> {
116 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
117 use ParserError::*;
118 match self {
119 AtRuleBodyInvalid => write!(f, "Invalid @ rule body"),
120 AtRulePreludeInvalid => write!(f, "Invalid @ rule prelude"),
121 AtRuleInvalid(name) => write!(f, "Unknown at rule: @{}", name),
122 EndOfInput => write!(f, "Unexpected end of input"),
123 InvalidDeclaration => write!(f, "Invalid declaration"),
124 InvalidMediaQuery => write!(f, "Invalid media query"),
125 EmptyBracketInCondition => write!(f, "The brackets cannot be empty"),
126 InvalidNesting => write!(f, "Invalid nesting"),
127 DeprecatedNestRule => write!(f, "The @nest rule is deprecated"),
128 DeprecatedCssModulesValueRule => write!(f, "The @value rule is deprecated"),
129 InvalidPageSelector => write!(f, "Invalid page selector"),
130 InvalidValue => write!(f, "Invalid value"),
131 QualifiedRuleInvalid => write!(f, "Invalid qualified rule"),
132 SelectorError(s) => s.fmt(f),
133 UnexpectedImportRule => write!(
134 f,
135 "@import rules must precede all rules aside from @charset and @layer statements"
136 ),
137 UnexpectedNamespaceRule => write!(
138 f,
139 "@namespaces rules must precede all rules aside from @charset, @import, and @layer statements"
140 ),
141 UnexpectedToken(token) => write!(f, "Unexpected token {:?}", token),
142 MaximumNestingDepth => write!(f, "Overflowed the maximum nesting depth"),
143 }
144 }
145}
146
147impl<'i> Error<ParserError<'i>> {
148 pub fn from(err: ParseError<'i, ParserError<'i>>, filename: String) -> Error<ParserError<'i>> {
150 let kind = match err.kind {
151 ParseErrorKind::Basic(b) => match &b {
152 BasicParseErrorKind::UnexpectedToken(t) => ParserError::UnexpectedToken(t.into()),
153 BasicParseErrorKind::EndOfInput => ParserError::EndOfInput,
154 BasicParseErrorKind::AtRuleInvalid(a) => ParserError::AtRuleInvalid(a.into()),
155 BasicParseErrorKind::AtRuleBodyInvalid => ParserError::AtRuleBodyInvalid,
156 BasicParseErrorKind::QualifiedRuleInvalid => ParserError::QualifiedRuleInvalid,
157 },
158 ParseErrorKind::Custom(c) => c,
159 };
160
161 Error {
162 kind,
163 loc: Some(ErrorLocation {
164 filename,
165 line: err.location.line,
166 column: err.location.column,
167 }),
168 }
169 }
170
171 #[cfg(feature = "into_owned")]
173 pub fn into_owned<'x>(self) -> Error<ParserError<'static>> {
174 Error {
175 kind: self.kind.into_owned(),
176 loc: self.loc,
177 }
178 }
179}
180
181impl<'i> From<SelectorParseErrorKind<'i>> for ParserError<'i> {
182 fn from(err: SelectorParseErrorKind<'i>) -> ParserError<'i> {
183 ParserError::SelectorError(err.into())
184 }
185}
186
187impl<'i> ParserError<'i> {
188 #[deprecated(note = "use `ParserError::to_string()` or `fmt::Display` instead")]
189 #[allow(missing_docs)]
190 pub fn reason(&self) -> String {
191 self.to_string()
192 }
193}
194
195#[derive(Debug, PartialEq, Clone)]
197#[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))]
198#[cfg_attr(any(feature = "serde", feature = "nodejs"), derive(Serialize))]
199#[cfg_attr(any(feature = "serde", feature = "nodejs"), serde(tag = "type", content = "value"))]
200pub enum SelectorError<'i> {
201 BadValueInAttr(#[cfg_attr(any(feature = "serde", feature = "nodejs"), serde(skip))] Token<'i>),
203 ClassNeedsIdent(#[cfg_attr(any(feature = "serde", feature = "nodejs"), serde(skip))] Token<'i>),
205 DanglingCombinator,
207 EmptySelector,
209 ExpectedBarInAttr(#[cfg_attr(any(feature = "serde", feature = "nodejs"), serde(skip))] Token<'i>),
211 ExpectedNamespace(CowArcStr<'i>),
213 ExplicitNamespaceUnexpectedToken(#[cfg_attr(any(feature = "serde", feature = "nodejs"), serde(skip))] Token<'i>),
215 InvalidPseudoClassAfterPseudoElement,
217 InvalidPseudoClassAfterWebKitScrollbar,
219 InvalidPseudoClassBeforeWebKitScrollbar,
221 InvalidQualNameInAttr(#[cfg_attr(any(feature = "serde", feature = "nodejs"), serde(skip))] Token<'i>),
223 InvalidState,
225 MissingNestingPrefix,
227 MissingNestingSelector,
229 NoQualifiedNameInAttributeSelector(
231 #[cfg_attr(any(feature = "serde", feature = "nodejs"), serde(skip))] Token<'i>,
232 ),
233 PseudoElementExpectedIdent(#[cfg_attr(any(feature = "serde", feature = "nodejs"), serde(skip))] Token<'i>),
235 UnexpectedIdent(CowArcStr<'i>),
237 UnexpectedTokenInAttributeSelector(
239 #[cfg_attr(any(feature = "serde", feature = "nodejs"), serde(skip))] Token<'i>,
240 ),
241
242 UnsupportedPseudoClass(CowArcStr<'i>),
244
245 UnsupportedPseudoElement(CowArcStr<'i>),
247
248 AmbiguousCssModuleClass(CowArcStr<'i>),
250
251 UnexpectedSelectorAfterPseudoElement(
253 #[cfg_attr(any(feature = "serde", feature = "nodejs"), serde(skip))] Token<'i>,
254 ),
255}
256
257impl<'i> fmt::Display for SelectorError<'i> {
258 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
259 use SelectorError::*;
260 match self {
261 InvalidState => write!(f, "Invalid state"),
262 BadValueInAttr(token) => write!(f, "Invalid value in attribute selector: {:?}", token),
263 ClassNeedsIdent(token) => write!(f, "Expected identifier in class selector, got {:?}", token),
264 DanglingCombinator => write!(f, "Invalid dangling combinator in selector"),
265 EmptySelector => write!(f, "Invalid empty selector"),
266 ExpectedBarInAttr(name) => write!(f, "Expected | in attribute, got {:?}", name),
267 ExpectedNamespace(name) => write!(f, "Expected namespace: {}", name),
268 ExplicitNamespaceUnexpectedToken(token) => write!(f, "Unexpected token in namespace selector: {:?}", token),
269 InvalidPseudoClassAfterPseudoElement => write!(f, "Invalid pseudo class after pseudo element, only user action pseudo classes (e.g. :hover, :active) are allowed"),
270 InvalidPseudoClassAfterWebKitScrollbar => write!(f, "Invalid pseudo class after ::-webkit-scrollbar pseudo element"),
271 InvalidPseudoClassBeforeWebKitScrollbar => write!(f, "Pseudo class must be prefixed by a ::-webkit-scrollbar pseudo element"),
272 InvalidQualNameInAttr(token) => write!(f, "Invalid qualified name in attribute selector: {:?}", token),
273 MissingNestingPrefix => write!(f, "A nested rule must start with a nesting selector (&) as prefix of each selector, or start with @nest"),
274 MissingNestingSelector => write!(f, "A nesting selector (&) is required in each selector of a @nest rule"),
275 NoQualifiedNameInAttributeSelector(token) => write!(f, "No qualified name in attribute selector: {:?}.", token),
276 PseudoElementExpectedIdent(token) => write!(f, "Invalid token in pseudo element: {:?}", token),
277 UnexpectedIdent(name) => write!(f, "Unexpected identifier: {}", name),
278 UnexpectedTokenInAttributeSelector(token) => write!(f, "Unexpected token in attribute selector: {:?}", token),
279 UnsupportedPseudoClass(name) =>write!(f, "'{name}' is not recognized as a valid pseudo-class. Did you mean '::{name}' (pseudo-element) or is this a typo?"),
280 UnsupportedPseudoElement(name) => write!(f, "'{name}' is not recognized as a valid pseudo-element. Did you mean ':{name}' (pseudo-class) or is this a typo?"),
281 AmbiguousCssModuleClass(_) => write!(f, "Ambiguous CSS module class not supported"),
282 UnexpectedSelectorAfterPseudoElement(token) => {
283 write!(
284 f,
285 "Pseudo-elements like '::before' or '::after' can't be followed by selectors like '{token:?}'"
286 )
287 },
288 }
289 }
290}
291
292impl<'i> From<SelectorParseErrorKind<'i>> for SelectorError<'i> {
293 fn from(err: SelectorParseErrorKind<'i>) -> Self {
294 match &err {
295 SelectorParseErrorKind::NoQualifiedNameInAttributeSelector(t) => {
296 SelectorError::NoQualifiedNameInAttributeSelector(t.into())
297 }
298 SelectorParseErrorKind::EmptySelector => SelectorError::EmptySelector,
299 SelectorParseErrorKind::DanglingCombinator => SelectorError::DanglingCombinator,
300 SelectorParseErrorKind::InvalidPseudoClassBeforeWebKitScrollbar => {
301 SelectorError::InvalidPseudoClassBeforeWebKitScrollbar
302 }
303 SelectorParseErrorKind::InvalidPseudoClassAfterWebKitScrollbar => {
304 SelectorError::InvalidPseudoClassAfterWebKitScrollbar
305 }
306 SelectorParseErrorKind::InvalidPseudoClassAfterPseudoElement => {
307 SelectorError::InvalidPseudoClassAfterPseudoElement
308 }
309 SelectorParseErrorKind::InvalidState => SelectorError::InvalidState,
310 SelectorParseErrorKind::MissingNestingSelector => SelectorError::MissingNestingSelector,
311 SelectorParseErrorKind::MissingNestingPrefix => SelectorError::MissingNestingPrefix,
312 SelectorParseErrorKind::UnexpectedTokenInAttributeSelector(t) => {
313 SelectorError::UnexpectedTokenInAttributeSelector(t.into())
314 }
315 SelectorParseErrorKind::PseudoElementExpectedIdent(t) => SelectorError::PseudoElementExpectedIdent(t.into()),
316 SelectorParseErrorKind::UnsupportedPseudoClass(t) => SelectorError::UnsupportedPseudoClass(t.into()),
317 SelectorParseErrorKind::UnsupportedPseudoElement(t) => SelectorError::UnsupportedPseudoElement(t.into()),
318 SelectorParseErrorKind::UnexpectedIdent(t) => SelectorError::UnexpectedIdent(t.into()),
319 SelectorParseErrorKind::ExpectedNamespace(t) => SelectorError::ExpectedNamespace(t.into()),
320 SelectorParseErrorKind::ExpectedBarInAttr(t) => SelectorError::ExpectedBarInAttr(t.into()),
321 SelectorParseErrorKind::BadValueInAttr(t) => SelectorError::BadValueInAttr(t.into()),
322 SelectorParseErrorKind::InvalidQualNameInAttr(t) => SelectorError::InvalidQualNameInAttr(t.into()),
323 SelectorParseErrorKind::ExplicitNamespaceUnexpectedToken(t) => {
324 SelectorError::ExplicitNamespaceUnexpectedToken(t.into())
325 }
326 SelectorParseErrorKind::ClassNeedsIdent(t) => SelectorError::ClassNeedsIdent(t.into()),
327 SelectorParseErrorKind::AmbiguousCssModuleClass(name) => SelectorError::AmbiguousCssModuleClass(name.into()),
328 SelectorParseErrorKind::UnexpectedSelectorAfterPseudoElement(t) => {
329 SelectorError::UnexpectedSelectorAfterPseudoElement(t.into())
330 }
331 }
332 }
333}
334
335#[derive(Debug, PartialEq)]
336pub(crate) struct ErrorWithLocation<T> {
337 pub kind: T,
338 pub loc: Location,
339}
340
341impl<T: fmt::Display> fmt::Display for ErrorWithLocation<T> {
342 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
343 self.kind.fmt(f)
344 }
345}
346
347impl<T: fmt::Display + fmt::Debug> std::error::Error for ErrorWithLocation<T> {}
348
349pub(crate) type MinifyError = ErrorWithLocation<MinifyErrorKind>;
350
351#[derive(Debug, PartialEq)]
353#[cfg_attr(any(feature = "serde", feature = "nodejs"), derive(Serialize))]
354#[cfg_attr(any(feature = "serde", feature = "nodejs"), serde(tag = "type"))]
355pub enum MinifyErrorKind {
356 CircularCustomMedia {
358 name: String,
360 },
361 CustomMediaNotDefined {
363 name: String,
365 },
366 UnsupportedCustomMediaBooleanLogic {
368 custom_media_loc: Location,
370 },
371 ImpureCSSModuleSelector,
373}
374
375impl fmt::Display for MinifyErrorKind {
376 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
377 use MinifyErrorKind::*;
378 match self {
379 CircularCustomMedia { name } => write!(f, "Circular custom media query {} detected", name),
380 CustomMediaNotDefined { name } => write!(f, "Custom media query {} is not defined", name),
381 UnsupportedCustomMediaBooleanLogic { .. } => write!(
382 f,
383 "Boolean logic with media types in @custom-media rules is not supported by Lightning CSS"
384 ),
385 ImpureCSSModuleSelector => write!(
386 f,
387 "A selector in CSS modules should contain at least one class or ID selector"
388 ),
389 }
390 }
391}
392
393impl MinifyErrorKind {
394 #[deprecated(note = "use `MinifyErrorKind::to_string()` or `fmt::Display` instead")]
395 #[allow(missing_docs)]
396 pub fn reason(&self) -> String {
397 self.to_string()
398 }
399}
400
401pub type PrinterError = Error<PrinterErrorKind>;
403
404#[derive(Debug, PartialEq)]
406#[cfg_attr(any(feature = "serde", feature = "nodejs"), derive(Serialize))]
407#[cfg_attr(any(feature = "serde", feature = "nodejs"), serde(tag = "type"))]
408pub enum PrinterErrorKind {
409 AmbiguousUrlInCustomProperty {
411 url: String,
413 },
414 FmtError,
416 InvalidComposesNesting,
418 InvalidComposesSelector,
420 InvalidCssModulesPatternInGrid,
422}
423
424impl From<fmt::Error> for PrinterError {
425 fn from(_: fmt::Error) -> PrinterError {
426 PrinterError {
427 kind: PrinterErrorKind::FmtError,
428 loc: None,
429 }
430 }
431}
432
433impl fmt::Display for PrinterErrorKind {
434 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
435 use PrinterErrorKind::*;
436 match self {
437 AmbiguousUrlInCustomProperty { url } => write!(f, "Ambiguous url('{}') in custom property. Relative paths are resolved from the location the var() is used, not where the custom property is defined. Use an absolute URL instead", url),
438 FmtError => write!(f, "Printer error"),
439 InvalidComposesNesting => write!(f, "The `composes` property cannot be used within nested rules"),
440 InvalidComposesSelector => write!(f, "The `composes` property cannot be used with a simple class selector"),
441 InvalidCssModulesPatternInGrid => write!(f, "The CSS modules `pattern` config must end with `[local]` for use in CSS grid line names."),
442 }
443 }
444}
445
446impl PrinterErrorKind {
447 #[deprecated(note = "use `PrinterErrorKind::to_string()` or `fmt::Display` instead")]
448 #[allow(missing_docs)]
449 pub fn reason(&self) -> String {
450 self.to_string()
451 }
452}