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