Files
bun.sh/src/css/declaration.zig
pfg 97c113d010 remove unused writer type parameters in src/css/ (#24571)
No longer needed after zig upgrade

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2025-11-11 21:09:50 -08:00

460 lines
17 KiB
Zig

pub const css = @import("./css_parser.zig");
pub const Error = css.Error;
const Printer = css.Printer;
const PrintErr = css.PrintErr;
const Result = css.Result;
pub const DeclarationList = ArrayList(css.Property);
const BackgroundHandler = css.css_properties.background.BackgroundHandler;
const BorderHandler = css.css_properties.border.BorderHandler;
const FallbackHandler = css.css_properties.prefix_handler.FallbackHandler;
const MarginHandler = css.css_properties.margin_padding.MarginHandler;
const PaddingHandler = css.css_properties.margin_padding.PaddingHandler;
const ScrollMarginHandler = css.css_properties.margin_padding.ScrollMarginHandler;
const FontHandler = css.css_properties.font.FontHandler;
const InsetHandler = css.css_properties.margin_padding.InsetHandler;
const SizeHandler = css.css_properties.size.SizeHandler;
const FlexHandler = css.css_properties.flex.FlexHandler;
const AlignHandler = css.css_properties.@"align".AlignHandler;
const TransitionHandler = css.css_properties.transition.TransitionHandler;
const TransformHandler = css.css_properties.transform.TransformHandler;
const ColorSchemeHandler = css.css_properties.ui.ColorSchemeHandler;
const BoxShadowHandler = css.css_properties.box_shadow.BoxShadowHandler;
// const GridHandler = css.css_properties.g
/// A CSS declaration block.
///
/// Properties are separated into a list of `!important` declararations,
/// and a list of normal declarations. This reduces memory usage compared
/// with storing a boolean along with each property.
///
/// TODO: multiarraylist will probably be faster here, as it makes one allocation
/// instead of two.
pub const DeclarationBlock = struct {
/// A list of `!important` declarations in the block.
important_declarations: ArrayList(css.Property) = .{},
/// A list of normal declarations in the block.
declarations: ArrayList(css.Property) = .{},
const This = @This();
const DebugFmt = struct {
self: *const DeclarationBlock,
pub fn format(this: @This(), writer: *std.Io.Writer) !void {
var arraylist = std.Io.Writer.Allocating.init(bun.default_allocator);
const w = &arraylist.writer;
defer arraylist.deinit();
var symbols = bun.ast.Symbol.Map{};
var printer = css.Printer.new(bun.default_allocator, std.array_list.Managed(u8).init(bun.default_allocator), w, css.PrinterOptions.default(), null, null, &symbols);
defer printer.deinit();
this.self.toCss(&printer) catch |e| return try writer.print("<error writing declaration block: {s}>\n", .{@errorName(e)});
try writer.writeAll(arraylist.written());
}
};
pub fn debug(this: *const @This()) DebugFmt {
return DebugFmt{ .self = this };
}
pub fn isEmpty(this: *const This) bool {
return this.declarations.items.len == 0 and this.important_declarations.items.len == 0;
}
pub fn parse(input: *css.Parser, options: *const css.ParserOptions) Result(DeclarationBlock) {
var important_declarations = DeclarationList{};
var declarations = DeclarationList{};
var decl_parser = PropertyDeclarationParser{
.important_declarations = &important_declarations,
.declarations = &declarations,
.options = options,
};
errdefer decl_parser.deinit();
var parser = css.RuleBodyParser(PropertyDeclarationParser).new(input, &decl_parser);
while (parser.next()) |res| {
if (res.asErr()) |e| {
if (options.error_recovery) {
options.warn(e);
continue;
}
return .{ .err = e };
}
}
return .{ .result = DeclarationBlock{
.important_declarations = important_declarations,
.declarations = declarations,
} };
}
pub fn len(this: *const DeclarationBlock) usize {
return this.declarations.items.len + this.important_declarations.items.len;
}
pub fn toCss(this: *const This, dest: *Printer) PrintErr!void {
const length = this.len();
var i: usize = 0;
const DECLS: []const []const u8 = &[_][]const u8{ "declarations", "important_declarations" };
inline for (DECLS) |decl_field_name| {
const decls = &@field(this, decl_field_name);
const is_important = comptime std.mem.eql(u8, decl_field_name, "important_declarations");
for (decls.items) |*decl| {
try decl.toCss(dest, is_important);
if (i != length - 1) {
try dest.writeChar(';');
try dest.whitespace();
}
i += 1;
}
}
return;
}
/// Writes the declarations to a CSS block, including starting and ending braces.
pub fn toCssBlock(this: *const This, dest: *Printer) PrintErr!void {
try dest.whitespace();
try dest.writeChar('{');
dest.indent();
var i: usize = 0;
const length = this.len();
const DECLS: []const []const u8 = &[_][]const u8{ "declarations", "important_declarations" };
inline for (DECLS) |decl_field_name| {
const decls = &@field(this, decl_field_name);
const is_important = comptime std.mem.eql(u8, decl_field_name, "important_declarations");
for (decls.items) |*decl| {
try dest.newline();
try decl.toCss(dest, is_important);
if (i != length - 1 or !dest.minify) {
try dest.writeChar(';');
}
i += 1;
}
}
dest.dedent();
try dest.newline();
return dest.writeChar('}');
}
pub fn minify(
this: *This,
handler: *DeclarationHandler,
important_handler: *DeclarationHandler,
context: *css.PropertyHandlerContext,
) void {
const handle = struct {
inline fn handle(
self: *This,
ctx: *css.PropertyHandlerContext,
hndlr: *DeclarationHandler,
comptime decl_field: []const u8,
comptime important: bool,
) void {
for (@field(self, decl_field).items) |*prop| {
ctx.is_important = important;
const handled = hndlr.handleProperty(prop, ctx);
if (!handled) {
bun.handleOom(hndlr.decls.append(ctx.allocator, prop.*));
// replacing with a property which does not require allocation
// to "delete"
prop.* = css.Property{ .all = .@"revert-layer" };
}
}
}
}.handle;
handle(this, context, important_handler, "important_declarations", true);
handle(this, context, handler, "declarations", false);
handler.finalize(context);
important_handler.finalize(context);
var old_import = this.important_declarations;
var old_declarations = this.declarations;
this.important_declarations = .{};
this.declarations = .{};
defer {
old_import.deinit(context.allocator);
old_declarations.deinit(context.allocator);
}
this.important_declarations = important_handler.decls;
this.declarations = handler.decls;
important_handler.decls = .{};
handler.decls = .{};
}
pub fn hashPropertyIds(this: *const @This(), hasher: *std.hash.Wyhash) void {
for (this.declarations.items) |*decl| {
decl.propertyId().hash(hasher);
}
for (this.important_declarations.items) |*decl| {
decl.propertyId().hash(hasher);
}
}
pub fn eql(this: *const This, other: *const This) bool {
return css.implementEql(@This(), this, other);
}
pub fn deepClone(this: *const This, allocator: std.mem.Allocator) This {
return css.implementDeepClone(@This(), this, allocator);
}
};
pub const PropertyDeclarationParser = struct {
important_declarations: *ArrayList(css.Property),
declarations: *ArrayList(css.Property),
options: *const css.ParserOptions,
const This = @This();
pub const AtRuleParser = struct {
pub const Prelude = void;
pub const AtRule = void;
pub fn parsePrelude(_: *This, name: []const u8, input: *css.Parser) Result(Prelude) {
return .{
.err = input.newError(css.BasicParseErrorKind{ .at_rule_invalid = name }),
};
}
pub fn parseBlock(_: *This, _: Prelude, _: *const css.ParserState, input: *css.Parser) Result(AtRule) {
return .{ .err = input.newError(css.BasicParseErrorKind.at_rule_body_invalid) };
}
pub fn ruleWithoutBlock(_: *This, _: Prelude, _: *const css.ParserState) css.Maybe(AtRule, void) {
return .{ .err = {} };
}
};
pub const QualifiedRuleParser = struct {
pub const Prelude = void;
pub const QualifiedRule = void;
pub fn parsePrelude(this: *This, input: *css.Parser) Result(Prelude) {
_ = this; // autofix
return .{ .err = input.newError(css.BasicParseErrorKind.qualified_rule_invalid) };
}
pub fn parseBlock(this: *This, prelude: Prelude, start: *const css.ParserState, input: *css.Parser) Result(QualifiedRule) {
_ = this; // autofix
_ = prelude; // autofix
_ = start; // autofix
return .{ .err = input.newError(css.BasicParseErrorKind.qualified_rule_invalid) };
}
};
pub const DeclarationParser = struct {
pub const Declaration = void;
pub fn parseValue(this: *This, name: []const u8, input: *css.Parser) Result(Declaration) {
return parse_declaration(
name,
input,
this.declarations,
this.important_declarations,
this.options,
);
}
};
pub const RuleBodyItemParser = struct {
pub fn parseQualified(this: *This) bool {
_ = this; // autofix
return false;
}
pub fn parseDeclarations(this: *This) bool {
_ = this; // autofix
return true;
}
};
};
pub fn parse_declaration(
name: []const u8,
input: *css.Parser,
declarations: *DeclarationList,
important_declarations: *DeclarationList,
options: *const css.ParserOptions,
) Result(void) {
return parse_declaration_impl(name, input, declarations, important_declarations, options, {});
}
pub fn parse_declaration_impl(
name: []const u8,
input: *css.Parser,
declarations: *DeclarationList,
important_declarations: *DeclarationList,
options: *const css.ParserOptions,
composes_ctx: anytype,
) Result(void) {
const property_id = css.PropertyId.fromStr(name);
var delimiters = css.Delimiters{ .bang = true };
if (property_id != .custom or property_id.custom != .custom) {
delimiters.curly_bracket = true;
}
const Closure = struct {
property_id: css.PropertyId,
options: *const css.ParserOptions,
};
var closure = Closure{
.property_id = property_id,
.options = options,
};
const source_location = input.currentSourceLocation();
var property = switch (input.parseUntilBefore(delimiters, css.Property, &closure, struct {
pub fn parseFn(this: *Closure, input2: *css.Parser) Result(css.Property) {
return css.Property.parse(this.property_id, input2, this.options);
}
}.parseFn)) {
.err => |e| return .{ .err = e },
.result => |v| v,
};
const important = input.tryParse(struct {
pub fn parsefn(i: *css.Parser) Result(void) {
if (i.expectDelim('!').asErr()) |e| return .{ .err = e };
return i.expectIdentMatching("important");
}
}.parsefn, .{}).isOk();
if (input.expectExhausted().asErr()) |e| return .{ .err = e };
if (comptime @TypeOf(composes_ctx) != void) {
if (input.flags.css_modules and property == .composes) {
switch (composes_ctx.composes_state) {
.disallow_entirely => {},
.allow => {
composes_ctx.recordComposes(input.allocator(), &property.composes);
},
.disallow_nested => |info| {
options.warnFmtWithNotes(
"\"composes\" is not allowed inside nested selectors",
.{},
info.line,
info.column,
&[_]bun.logger.Data{},
);
},
.disallow_not_single_class => |info| {
options.warnFmtWithNotes(
"\"composes\" only works inside single class selectors",
.{},
source_location.line,
source_location.column,
options.allocator.dupe(
bun.logger.Data,
&[_]bun.logger.Data{
bun.logger.Data{
.text = bun.handleOom(options.allocator.dupe(u8, "The parent selector is not a single class selector because of the syntax here:")),
.location = info.toLoggerLocation(options.filename),
},
},
) catch |err| bun.handleOom(err),
);
},
}
}
}
if (important) {
bun.handleOom(important_declarations.append(input.allocator(), property));
} else {
bun.handleOom(declarations.append(input.allocator(), property));
}
return .success;
}
pub const DeclarationHandler = struct {
background: BackgroundHandler = .{},
border: BorderHandler = .{},
flex: FlexHandler = .{},
@"align": AlignHandler = .{},
size: SizeHandler = .{},
margin: MarginHandler = .{},
padding: PaddingHandler = .{},
scroll_margin: ScrollMarginHandler = .{},
transition: TransitionHandler = .{},
font: FontHandler = .{},
inset: InsetHandler = .{},
transform: TransformHandler = .{},
box_shadow: BoxShadowHandler = .{},
color_scheme: ColorSchemeHandler = .{},
fallback: FallbackHandler = .{},
direction: ?css.css_properties.text.Direction,
decls: DeclarationList,
pub fn finalize(this: *DeclarationHandler, context: *css.PropertyHandlerContext) void {
const allocator = context.allocator;
_ = allocator; // autofix
if (this.direction) |direction| {
this.direction = null;
bun.handleOom(this.decls.append(context.allocator, css.Property{ .direction = direction }));
}
// if (this.unicode_bidi) |unicode_bidi| {
// this.unicode_bidi = null;
// this.decls.append(context.allocator, css.Property{ .unicode_bidi = unicode_bidi }) catch |err| bun.handleOom(err);
// }
this.background.finalize(&this.decls, context);
this.border.finalize(&this.decls, context);
this.flex.finalize(&this.decls, context);
this.@"align".finalize(&this.decls, context);
this.size.finalize(&this.decls, context);
this.margin.finalize(&this.decls, context);
this.padding.finalize(&this.decls, context);
this.scroll_margin.finalize(&this.decls, context);
this.transition.finalize(&this.decls, context);
this.font.finalize(&this.decls, context);
this.inset.finalize(&this.decls, context);
this.transform.finalize(&this.decls, context);
this.box_shadow.finalize(&this.decls, context);
this.color_scheme.finalize(&this.decls, context);
this.fallback.finalize(&this.decls, context);
}
pub fn handleProperty(this: *DeclarationHandler, property: *const css.Property, context: *css.PropertyHandlerContext) bool {
// return this.background.handleProperty(property, &this.decls, context);
return this.background.handleProperty(property, &this.decls, context) or
this.border.handleProperty(property, &this.decls, context) or
this.flex.handleProperty(property, &this.decls, context) or
this.@"align".handleProperty(property, &this.decls, context) or
this.size.handleProperty(property, &this.decls, context) or
this.margin.handleProperty(property, &this.decls, context) or
this.padding.handleProperty(property, &this.decls, context) or
this.scroll_margin.handleProperty(property, &this.decls, context) or
this.transition.handleProperty(property, &this.decls, context) or
this.font.handleProperty(property, &this.decls, context) or
this.inset.handleProperty(property, &this.decls, context) or
this.transform.handleProperty(property, &this.decls, context) or
this.box_shadow.handleProperty(property, &this.decls, context) or
this.color_scheme.handleProperty(property, &this.decls, context) or
this.fallback.handleProperty(property, &this.decls, context);
}
pub fn default() DeclarationHandler {
return .{
.decls = .{},
.direction = null,
};
}
};
const bun = @import("bun");
const logger = bun.logger;
const std = @import("std");
const ArrayList = std.ArrayListUnmanaged;
const Allocator = std.mem.Allocator;