Files
bun.sh/src/css/error.zig
Zack Radisic a01f9d8e1b Integrate CSS with bundler (#14281)
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
Co-authored-by: Zack Radisic <zackradisic@Mac.attlocal.net>
Co-authored-by: zackradisic <zackradisic@users.noreply.github.com>
Co-authored-by: Zack Radisic <zackradisic@Zacks-MBP.attlocal.net>
2024-10-04 20:23:10 -07:00

326 lines
12 KiB
Zig

const std = @import("std");
const Allocator = std.mem.Allocator;
const bun = @import("root").bun;
const logger = bun.logger;
const Log = logger.Log;
pub const css = @import("./css_parser.zig");
pub const css_values = @import("./values/values.zig");
const DashedIdent = css_values.ident.DashedIdent;
const Ident = css_values.ident.Ident;
pub const Error = css.Error;
const Location = css.Location;
const ArrayList = std.ArrayListUnmanaged;
/// A printer error.
pub const PrinterError = Err(PrinterErrorKind);
pub fn fmtPrinterError() PrinterError {
return .{
.kind = .fmt_error,
.loc = null,
};
}
/// An error with a source location.
pub fn Err(comptime T: type) type {
return struct {
/// The type of error that occurred.
kind: T,
/// The location where the error occurred.
loc: ?ErrorLocation,
pub fn fromParseError(err: ParseError(ParserError), filename: []const u8) Err(ParserError) {
if (T != ParserError) {
@compileError("Called .fromParseError() when T is not ParserError");
}
const kind = switch (err.kind) {
.basic => |b| switch (b) {
.unexpected_token => |t| ParserError{ .unexpected_token = t },
.end_of_input => ParserError.end_of_input,
.at_rule_invalid => |a| ParserError{ .at_rule_invalid = a },
.at_rule_body_invalid => ParserError.at_rule_body_invalid,
.qualified_rule_invalid => ParserError.qualified_rule_invalid,
},
.custom => |c| c,
};
return .{
.kind = kind,
.loc = ErrorLocation{
.filename = filename,
.line = err.location.line,
.column = err.location.column,
},
};
}
};
}
/// Extensible parse errors that can be encountered by client parsing implementations.
pub fn ParseError(comptime T: type) type {
return struct {
/// Details of this error
kind: ParserErrorKind(T),
/// Location where this error occurred
location: css.SourceLocation,
pub fn basic(this: @This()) BasicParseError {
return switch (this.kind) {
.basic => |kind| BasicParseError{
.kind = kind,
.location = this.location,
},
.custom => @panic("Not a basic parse error. This is a bug in Bun's css parser."),
};
}
};
}
pub fn ParserErrorKind(comptime T: type) type {
return union(enum) {
/// A fundamental parse error from a built-in parsing routine.
basic: BasicParseErrorKind,
/// A parse error reported by downstream consumer code.
custom: T,
pub fn format(this: @This(), comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void {
return switch (this) {
.basic => |basic| writer.print("basic: {}", .{basic}),
.custom => |custom| writer.print("custom: {}", .{custom}),
};
}
};
}
/// Details about a `BasicParseError`
pub const BasicParseErrorKind = union(enum) {
/// An unexpected token was encountered.
unexpected_token: css.Token,
/// The end of the input was encountered unexpectedly.
end_of_input,
/// An `@` rule was encountered that was invalid.
at_rule_invalid: []const u8,
/// The body of an '@' rule was invalid.
at_rule_body_invalid,
/// A qualified rule was encountered that was invalid.
qualified_rule_invalid,
pub fn format(this: *const BasicParseErrorKind, comptime fmt: []const u8, opts: std.fmt.FormatOptions, writer: anytype) !void {
_ = fmt; // autofix
_ = opts; // autofix
return switch (this.*) {
.unexpected_token => |token| {
try writer.print("unexpected token: {}", .{token});
},
.end_of_input => {
try writer.print("unexpected end of input", .{});
},
.at_rule_invalid => |rule| {
try writer.print("invalid @ rule encountered: '@{s}'", .{rule});
},
.at_rule_body_invalid => {
// try writer.print("invalid @ body rule encountered: '@{s}'", .{});
try writer.print("invalid @ body rule encountered", .{});
},
.qualified_rule_invalid => {
try writer.print("invalid qualified rule encountered", .{});
},
};
}
};
/// A line and column location within a source file.
pub const ErrorLocation = struct {
/// The filename in which the error occurred.
filename: []const u8,
/// The line number, starting from 0.
line: u32,
/// The column number, starting from 1.
column: u32,
};
/// A printer error type.
pub const PrinterErrorKind = union(enum) {
/// An ambiguous relative `url()` was encountered in a custom property declaration.
ambiguous_url_in_custom_property: struct {
/// The ambiguous URL.
url: []const u8,
},
/// A [std::fmt::Error](std::fmt::Error) was encountered in the underlying destination.
fmt_error,
/// The CSS modules `composes` property cannot be used within nested rules.
invalid_composes_nesting,
/// The CSS modules `composes` property cannot be used with a simple class selector.
invalid_composes_selector,
/// The CSS modules pattern must end with `[local]` for use in CSS grid.
invalid_css_modules_pattern_in_grid,
no_import_records,
};
/// A parser error.
pub const ParserError = union(enum) {
/// An at rule body was invalid.
at_rule_body_invalid,
/// An at rule prelude was invalid.
at_rule_prelude_invalid,
/// An unknown or unsupported at rule was encountered.
at_rule_invalid: []const u8,
/// Unexpectedly encountered the end of input data.
end_of_input,
/// A declaration was invalid.
invalid_declaration,
/// A media query was invalid.
invalid_media_query,
/// Invalid CSS nesting.
invalid_nesting,
/// The @nest rule is deprecated.
deprecated_nest_rule,
/// An invalid selector in an `@page` rule.
invalid_page_selector,
/// An invalid value was encountered.
invalid_value,
/// Invalid qualified rule.
qualified_rule_invalid,
/// A selector was invalid.
selector_error: SelectorError,
/// An `@import` rule was encountered after any rule besides `@charset` or `@layer`.
unexpected_import_rule,
/// A `@namespace` rule was encountered after any rules besides `@charset`, `@import`, or `@layer`.
unexpected_namespace_rule,
/// An unexpected token was encountered.
unexpected_token: css.Token,
/// Maximum nesting depth was reached.
maximum_nesting_depth,
pub fn format(this: @This(), comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void {
return switch (this) {
.at_rule_invalid => |name| writer.print("at_rule_invalid: {s}", .{name}),
.unexpected_token => |token| writer.print("unexpected_token: {}", .{token}),
.selector_error => |err| writer.print("selector_error: {}", .{err}),
else => writer.print("{s}", .{@tagName(this)}),
};
}
};
/// The fundamental parsing errors that can be triggered by built-in parsing routines.
pub const BasicParseError = struct {
/// Details of this error
kind: BasicParseErrorKind,
/// Location where this error occurred
location: css.SourceLocation,
pub fn intoParseError(
this: @This(),
comptime T: type,
) ParseError(T) {
return ParseError(T){
.kind = .{ .basic = this.kind },
.location = this.location,
};
}
pub inline fn intoDefaultParseError(
this: @This(),
) ParseError(ParserError) {
return ParseError(ParserError){
.kind = .{ .basic = this.kind },
.location = this.location,
};
}
};
/// A selector parsing error.
pub const SelectorError = union(enum) {
/// An unexpected token was found in an attribute selector.
bad_value_in_attr: css.Token,
/// An unexpected token was found in a class selector.
class_needs_ident: css.Token,
/// A dangling combinator was found.
dangling_combinator,
/// An empty selector.
empty_selector,
/// A `|` was expected in an attribute selector.
expected_bar_in_attr: css.Token,
/// A namespace was expected.
expected_namespace: []const u8,
/// An unexpected token was encountered in a namespace.
explicit_namespace_unexpected_token: css.Token,
/// An invalid pseudo class was encountered after a pseudo element.
invalid_pseudo_class_after_pseudo_element,
/// An invalid pseudo class was encountered after a `-webkit-scrollbar` pseudo element.
invalid_pseudo_class_after_webkit_scrollbar,
/// A `-webkit-scrollbar` state was encountered before a `-webkit-scrollbar` pseudo element.
invalid_pseudo_class_before_webkit_scrollbar,
/// Invalid qualified name in attribute selector.
invalid_qual_name_in_attr: css.Token,
/// The current token is not allowed in this state.
invalid_state,
/// The selector is required to have the `&` nesting selector at the start.
missing_nesting_prefix,
/// The selector is missing a `&` nesting selector.
missing_nesting_selector,
/// No qualified name in attribute selector.
no_qualified_name_in_attribute_selector: css.Token,
/// An invalid token was encountered in a pseudo element.
pseudo_element_expected_ident: css.Token,
/// An unexpected identifier was encountered.
unexpected_ident: []const u8,
/// An unexpected token was encountered inside an attribute selector.
unexpected_token_in_attribute_selector: css.Token,
/// An unsupported pseudo class or pseudo element was encountered.
unsupported_pseudo_class_or_element: []const u8,
pub fn format(this: @This(), comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void {
return switch (this) {
.dangling_combinator, .empty_selector, .invalid_state, .missing_nesting_prefix, .missing_nesting_selector => {
try writer.print("{s}", .{@tagName(this)});
},
inline .expected_namespace, .unexpected_ident, .unsupported_pseudo_class_or_element => |str| {
try writer.print("{s}: {s}", .{ @tagName(this), str });
},
inline .bad_value_in_attr,
.class_needs_ident,
.expected_bar_in_attr,
.explicit_namespace_unexpected_token,
.invalid_qual_name_in_attr,
.no_qualified_name_in_attribute_selector,
.pseudo_element_expected_ident,
.unexpected_token_in_attribute_selector,
=> |tok| {
try writer.print("{s}: {s}", .{ @tagName(this), @tagName(tok) });
},
else => try writer.print("{s}", .{@tagName(this)}),
};
}
};
pub fn ErrorWithLocation(comptime T: type) type {
return struct {
kind: T,
loc: css.Location,
};
}
pub const MinifyError = ErrorWithLocation(MinifyErrorKind);
/// A transformation error.
pub const MinifyErrorKind = union(enum) {
/// A circular `@custom-media` rule was detected.
circular_custom_media: struct {
/// The name of the `@custom-media` rule that was referenced circularly.
name: []const u8,
},
/// Attempted to reference a custom media rule that doesn't exist.
custom_media_not_defined: struct {
/// The name of the `@custom-media` rule that was not defined.
name: []const u8,
},
/// Boolean logic with media types in @custom-media rules is not supported.
unsupported_custom_media_boolean_logic: struct {
/// The source location of the `@custom-media` rule with unsupported boolean logic.
custom_media_loc: Location,
},
};