From 6b8fd718c2d5fa7f63d6f87b8f3ef0f4ecc27147 Mon Sep 17 00:00:00 2001 From: Zack Radisic <56137411+zackradisic@users.noreply.github.com> Date: Sat, 12 Oct 2024 07:00:20 -0700 Subject: [PATCH] Various CSS stuff (#14499) Co-authored-by: Jarred Sumner --- src/baby_list.zig | 28 + src/bitflags.zig | 16 + src/bun.zig | 6 + src/bundler.zig | 23 +- src/bundler/bundle_v2.zig | 44 +- src/css/context.zig | 193 + src/css/css_modules.zig | 8 +- src/css/css_parser.zig | 975 ++- src/css/declaration.zig | 100 +- src/css/dependencies.zig | 8 + src/css/error.zig | 30 + src/css/generics.zig | 411 + src/css/media_query.zig | 2 +- src/css/printer.zig | 18 +- src/css/properties/align.zig | 795 +- src/css/properties/animation.zig | 8 + src/css/properties/background.zig | 341 +- src/css/properties/border.zig | 229 +- src/css/properties/border_image.zig | 147 +- src/css/properties/border_radius.zig | 8 + src/css/properties/box_shadow.zig | 91 + src/css/properties/css_modules.zig | 22 +- src/css/properties/custom.zig | 105 +- src/css/properties/display.zig | 190 + src/css/properties/flex.zig | 346 +- src/css/properties/font.zig | 163 +- src/css/properties/generate_properties.ts | 1861 +++-- src/css/properties/margin_padding.zig | 165 +- src/css/properties/masking.zig | 408 +- src/css/properties/outline.zig | 15 + src/css/properties/overflow.zig | 13 +- src/css/properties/position.zig | 60 + src/css/properties/properties_generated.zig | 8251 ++++++++++++++++++- src/css/properties/size.zig | 237 +- src/css/properties/text.zig | 9 +- src/css/properties/transform.zig | 74 +- src/css/rules/container.zig | 24 + src/css/rules/counter_style.zig | 4 + src/css/rules/custom_media.zig | 8 + src/css/rules/document.zig | 4 + src/css/rules/font_face.zig | 20 + src/css/rules/font_palette_values.zig | 16 + src/css/rules/import.zig | 8 + src/css/rules/keyframes.zig | 16 + src/css/rules/layer.zig | 22 +- src/css/rules/media.zig | 6 +- src/css/rules/namespace.zig | 4 + src/css/rules/nesting.zig | 4 + src/css/rules/page.zig | 16 + src/css/rules/property.zig | 4 + src/css/rules/rules.zig | 410 +- src/css/rules/scope.zig | 10 +- src/css/rules/starting_style.zig | 4 + src/css/rules/style.zig | 99 +- src/css/rules/supports.zig | 31 +- src/css/rules/unknown.zig | 4 + src/css/rules/viewport.zig | 4 + src/css/selectors/builder.zig | 22 +- src/css/selectors/parser.zig | 407 +- src/css/selectors/selector.zig | 448 +- src/css/small_list.zig | 363 + src/css/targets.zig | 7 +- src/css/values/alpha.zig | 17 +- src/css/values/angle.zig | 8 + src/css/values/color.zig | 30 + src/css/values/gradient.zig | 284 +- src/css/values/ident.zig | 48 + src/css/values/image.zig | 115 +- src/css/values/length.zig | 53 + src/css/values/percentage.zig | 234 +- src/css/values/position.zig | 99 +- src/css/values/ratio.zig | 4 + src/css/values/rect.zig | 13 + src/css/values/resolution.zig | 21 + src/css/values/size.zig | 4 + src/css/values/syntax.zig | 20 + src/css/values/time.zig | 7 + src/css/values/url.zig | 16 + src/js_ast.zig | 18 +- src/linker.zig | 4 + src/meta.zig | 124 + test/bundler/esbuild/css.test.ts | 36 +- 82 files changed, 16775 insertions(+), 1745 deletions(-) create mode 100644 src/css/generics.zig create mode 100644 src/css/small_list.zig diff --git a/src/baby_list.zig b/src/baby_list.zig index a758fc8156..18c46df61f 100644 --- a/src/baby_list.zig +++ b/src/baby_list.zig @@ -14,6 +14,29 @@ pub fn BabyList(comptime Type: type) type { pub const Elem = Type; + pub fn parse(input: *bun.css.Parser) bun.css.Result(ListType) { + return switch (input.parseCommaSeparated(Type, bun.css.generic.parseFor(Type))) { + .result => |v| return .{ .result = ListType{ + .ptr = v.items.ptr, + .len = @intCast(v.items.len), + .cap = @intCast(v.capacity), + } }, + .err => |e| return .{ .err = e }, + }; + } + + pub fn toCss(this: *const ListType, comptime W: type, dest: *bun.css.Printer(W)) bun.css.PrintErr!void { + return bun.css.to_css.fromBabyList(Type, this, W, dest); + } + + pub fn eql(lhs: *const ListType, rhs: *const ListType) bool { + if (lhs.len != rhs.len) return false; + for (lhs.sliceConst(), rhs.sliceConst()) |*a, *b| { + if (!bun.css.generic.eql(Type, a, b)) return false; + } + return true; + } + pub fn set(this: *@This(), slice_: []Type) void { this.ptr = slice_.ptr; this.len = @as(u32, @truncate(slice_.len)); @@ -290,6 +313,11 @@ pub fn BabyList(comptime Type: type) type { return this.ptr[0..this.len]; } + pub fn sliceConst(this: *const ListType) callconv(bun.callconv_inline) []const Type { + @setRuntimeSafety(false); + return this.ptr[0..this.len]; + } + pub fn write(this: *@This(), allocator: std.mem.Allocator, str: []const u8) !u32 { if (comptime Type != u8) @compileError("Unsupported for type " ++ @typeName(Type)); diff --git a/src/bitflags.zig b/src/bitflags.zig index 01bf9e08e1..f7b1e2dc4c 100644 --- a/src/bitflags.zig +++ b/src/bitflags.zig @@ -39,6 +39,14 @@ pub fn Bitflags(comptime T: type) type { this.* = bitwiseOr(this.*, other); } + pub inline fn remove(this: *T, other: T) void { + this.* = bitwiseAnd(this.*, ~other); + } + + pub inline fn maskOut(this: T, other: T) T { + return @bitCast(asBits(this) & ~asBits(other)); + } + pub fn contains(lhs: T, rhs: T) bool { return @as(IntType, @bitCast(lhs)) & @as(IntType, @bitCast(rhs)) != 0; } @@ -55,8 +63,16 @@ pub fn Bitflags(comptime T: type) type { return asBits(lhs) == asBits(rhs); } + pub fn eql(lhs: T, rhs: T) bool { + return eq(lhs, rhs); + } + pub fn neq(lhs: T, rhs: T) bool { return asBits(lhs) != asBits(rhs); } + + pub fn hash(this: *const T, hasher: *std.hash.Wyhash) void { + hasher.update(std.mem.asBytes(this)); + } }; } diff --git a/src/bun.zig b/src/bun.zig index d3de7e70d9..65f76ce333 100644 --- a/src/bun.zig +++ b/src/bun.zig @@ -3929,6 +3929,12 @@ comptime { assert(GenericIndex(u32, opaque {}) != GenericIndex(u32, opaque {})); } +pub fn splitAtMut(comptime T: type, slice: []T, mid: usize) struct { []T, []T } { + bun.assert(mid <= slice.len); + + return .{ slice[0..mid], slice[mid..] }; +} + /// Reverse of the slice index operator. /// Given `&slice[index] == item`, returns the `index` needed. /// The item must be in the slice. diff --git a/src/bundler.zig b/src/bundler.zig index 4c66c6e0e2..a3178b101a 100644 --- a/src/bundler.zig +++ b/src/bundler.zig @@ -950,21 +950,24 @@ pub const Bundler = struct { }; const source = logger.Source.initRecycledFile(.{ .path = file_path, .contents = entry.contents }, bundler.allocator) catch return null; _ = source; // - switch (bun.css.StyleSheet(bun.css.DefaultAtRule).parse(alloc, entry.contents, bun.css.ParserOptions.default(alloc, bundler.log), null)) { - .result => |v| { - const result = v.toCss(alloc, bun.css.PrinterOptions{ - .minify = bun.getenvTruthy("BUN_CSS_MINIFY"), - }, null) catch |e| { - bun.handleErrorReturnTrace(e, @errorReturnTrace()); - return null; - }; - output_file.value = .{ .buffer = .{ .allocator = alloc, .bytes = result.code } }; - }, + var sheet = switch (bun.css.StyleSheet(bun.css.DefaultAtRule).parse(alloc, entry.contents, bun.css.ParserOptions.default(alloc, bundler.log), null)) { + .result => |v| v, .err => |e| { bundler.log.addErrorFmt(null, logger.Loc.Empty, bundler.allocator, "{} parsing", .{e}) catch unreachable; return null; }, + }; + if (sheet.minify(alloc, bun.css.MinifyOptions.default()).asErr()) |e| { + bundler.log.addErrorFmt(null, logger.Loc.Empty, bundler.allocator, "{} while minifying", .{e.kind}) catch bun.outOfMemory(); + return null; } + const result = sheet.toCss(alloc, bun.css.PrinterOptions{ + .minify = bun.getenvTruthy("BUN_CSS_MINIFY"), + }, null) catch |e| { + bun.handleErrorReturnTrace(e, @errorReturnTrace()); + return null; + }; + output_file.value = .{ .buffer = .{ .allocator = alloc, .bytes = result.code } }; } else { var file: bun.sys.File = undefined; diff --git a/src/bundler/bundle_v2.zig b/src/bundler/bundle_v2.zig index c8bdf02a01..de25df4ec2 100644 --- a/src/bundler/bundle_v2.zig +++ b/src/bundler/bundle_v2.zig @@ -3048,6 +3048,19 @@ pub const ParseTask = struct { threadlocal var override_file_path_buf: bun.PathBuffer = undefined; + fn getEmptyCSSAST( + log: *Logger.Log, + bundler: *Bundler, + opts: js_parser.Parser.Options, + allocator: std.mem.Allocator, + source: Logger.Source, + ) !JSAst { + const root = Expr.init(E.Object, E.Object{}, Logger.Loc{ .start = 0 }); + var ast = JSAst.init((try js_parser.newLazyExportAST(allocator, bundler.options.define, opts, log, root, &source, "")).?); + ast.css = bun.create(allocator, bun.css.BundlerStyleSheet, bun.css.BundlerStyleSheet.empty(allocator)); + return ast; + } + fn getEmptyAST(log: *Logger.Log, bundler: *Bundler, opts: js_parser.Parser.Options, allocator: std.mem.Allocator, source: Logger.Source, comptime RootType: type) !JSAst { const root = Expr.init(RootType, RootType{}, Logger.Loc.Empty); return JSAst.init((try js_parser.newLazyExportAST(allocator, bundler.options.define, opts, log, root, &source, "")).?); @@ -3104,7 +3117,7 @@ pub const ParseTask = struct { .data = source.contents, }, Logger.Loc{ .start = 0 }); var ast = JSAst.init((try js_parser.newLazyExportAST(allocator, bundler.options.define, opts, log, root, &source, "")).?); - ast.addUrlForCss(allocator, bundler.options.experimental_css, &source, "text/plain"); + ast.addUrlForCss(allocator, bundler.options.experimental_css, &source, "text/plain", null); return ast; }, @@ -3172,6 +3185,7 @@ pub const ParseTask = struct { return JSAst.init((try js_parser.newLazyExportAST(allocator, bundler.options.define, opts, log, root, &source, "")).?); }, .napi => { + // (dap-eval-cb "source.contents.ptr") if (bundler.options.target == .browser) { log.addError( null, @@ -3208,7 +3222,7 @@ pub const ParseTask = struct { const root = Expr.init(E.Object, E.Object{}, Logger.Loc{ .start = 0 }); var import_records = BabyList(ImportRecord){}; const source_code = source.contents; - const css_ast = + var css_ast = switch (bun.css.StyleSheet(bun.css.DefaultAtRule).parseBundler( allocator, source_code, @@ -3217,10 +3231,17 @@ pub const ParseTask = struct { )) { .result => |v| v, .err => |e| { - log.addErrorFmt(&source, Logger.Loc.Empty, allocator, "{}", .{e.kind}) catch unreachable; + log.addErrorFmt(&source, Logger.Loc.Empty, allocator, "{?}: {}", .{ if (e.loc) |l| l.withFilename(source.path.pretty) else null, e.kind }) catch unreachable; return error.SyntaxError; }, }; + if (css_ast.minify(allocator, bun.css.MinifyOptions{ + .targets = .{}, + .unused_symbols = .{}, + }).asErr()) |e| { + log.addErrorFmt(&source, Logger.Loc.Empty, allocator, "{?}: {}", .{ if (e.loc) |l| l.withFilename(source.path.pretty) else null, e.kind }) catch unreachable; + return error.MinifyError; + } const css_ast_heap = bun.create(allocator, bun.css.BundlerStyleSheet, css_ast); var ast = JSAst.init((try js_parser.newLazyExportAST(allocator, bundler.options.define, opts, log, root, &source, "")).?); ast.css = css_ast_heap; @@ -3236,7 +3257,8 @@ pub const ParseTask = struct { }, Logger.Loc{ .start = 0 }); unique_key_for_additional_file.* = unique_key; var ast = JSAst.init((try js_parser.newLazyExportAST(allocator, bundler.options.define, opts, log, root, &source, "")).?); - ast.addUrlForCss(allocator, bundler.options.experimental_css, &source, null); + ast.url_for_css = unique_key; + ast.addUrlForCss(allocator, bundler.options.experimental_css, &source, null, unique_key); return ast; } @@ -3420,7 +3442,13 @@ pub const ParseTask = struct { var ast: JSAst = if (!is_empty) try getAST(log, bundler, opts, allocator, resolver, source, loader, task.ctx.unique_key, &unique_key_for_additional_file) else switch (opts.module_type == .esm) { - inline else => |as_undefined| try getEmptyAST( + inline else => |as_undefined| if (loader == .css) try getEmptyCSSAST( + log, + bundler, + opts, + allocator, + source, + ) else try getEmptyAST( log, bundler, opts, @@ -6095,7 +6123,9 @@ pub const LinkerContext = struct { if (record.source_index.isValid()) { // Other file is not CSS if (css_asts[record.source_index.get()] == null) { - record.path.text = urls_for_css[record.source_index.get()]; + if (urls_for_css[record.source_index.get()]) |url| { + record.path.text = url; + } } } // else if (record.copy_source_index.isValid()) {} @@ -8457,7 +8487,7 @@ pub const LinkerContext = struct { if (item.layer) |l| { if (l.v) |layer| { if (ast.rules.v.items.len == 0) { - if (layer.v.items.len == 0) { + if (layer.v.isEmpty()) { // Omit an empty "@layer {}" entirely continue; } else { diff --git a/src/css/context.zig b/src/css/context.zig index a0d89d6d5a..98157f334e 100644 --- a/src/css/context.zig +++ b/src/css/context.zig @@ -8,10 +8,25 @@ pub const css = @import("./css_parser.zig"); const ArrayList = std.ArrayListUnmanaged; +const MediaRule = css.css_rules.media.MediaRule; +const MediaQuery = css.media_query.MediaQuery; +const MediaCondition = css.media_query.MediaCondition; +const MediaList = css.media_query.MediaList; +const MediaFeature = css.media_query.MediaFeature; +const MediaFeatureName = css.media_query.MediaFeatureName; +const MediaFeatureValue = css.media_query.MediaFeatureValue; +const MediaFeatureId = css.media_query.MediaFeatureId; + pub const SupportsEntry = struct { condition: css.SupportsCondition, declarations: ArrayList(css.Property), important_declarations: ArrayList(css.Property), + + pub fn deinit(this: *@This(), allocator: std.mem.Allocator) void { + _ = this; // autofix + _ = allocator; // autofix + @panic(css.todo_stuff.depth); + } }; pub const DeclarationContext = enum { @@ -49,4 +64,182 @@ pub const PropertyHandlerContext = struct { .unused_symbols = unused_symbols, }; } + + pub fn child(this: *const PropertyHandlerContext, context: DeclarationContext) PropertyHandlerContext { + return PropertyHandlerContext{ + .allocator = this.allocator, + .targets = this.targets, + .is_important = false, + .supports = .{}, + .ltr = .{}, + .rtl = .{}, + .dark = .{}, + .context = context, + .unused_symbols = this.unused_symbols, + }; + } + + pub fn getSupportsRules( + this: *const @This(), + comptime T: type, + style_rule: *const css.StyleRule(T), + ) ArrayList(css.CssRule(T)) { + if (this.supports.items.len == 0) { + return .{}; + } + + var dest = ArrayList(css.CssRule(T)).initCapacity( + this.allocator, + this.supports.items.len, + ) catch bun.outOfMemory(); + + for (this.supports.items) |*entry| { + dest.appendAssumeCapacity(css.CssRule(T){ + .supports = css.SupportsRule(T){ + .condition = entry.condition.deepClone(this.allocator), + .rules = css.CssRuleList(T){ + .v = v: { + var v = ArrayList(css.CssRule(T)).initCapacity(this.allocator, 1) catch bun.outOfMemory(); + + v.appendAssumeCapacity(.{ .style = css.StyleRule(T){ + .selectors = style_rule.selectors.deepClone(this.allocator), + .vendor_prefix = css.VendorPrefix{ .none = true }, + .declarations = css.DeclarationBlock{ + .declarations = css.deepClone(css.Property, this.allocator, &entry.declarations), + .important_declarations = css.deepClone(css.Property, this.allocator, &entry.important_declarations), + }, + .rules = css.CssRuleList(T){}, + .loc = style_rule.loc, + } }); + + break :v v; + }, + }, + .loc = style_rule.loc, + }, + }); + } + + return dest; + } + + pub fn getAdditionalRules( + this: *const @This(), + comptime T: type, + style_rule: *const css.StyleRule(T), + ) ArrayList(css.CssRule(T)) { + // TODO: :dir/:lang raises the specificity of the selector. Use :where to lower it? + var dest = ArrayList(css.CssRule(T)){}; + + if (this.ltr.items.len > 0) { + getAdditionalRulesHelper(this, T, "ltr", "ltr", style_rule, &dest); + } + + if (this.rtl.items.len > 0) { + getAdditionalRulesHelper(this, T, "rtl", "rtl", style_rule, &dest); + } + + if (this.dark.items.len > 0) { + dest.append(this.allocator, css.CssRule(T){ + .media = MediaRule(T){ + .query = MediaList{ + .media_queries = brk: { + var list = ArrayList(MediaQuery).initCapacity( + this.allocator, + 1, + ) catch bun.outOfMemory(); + + list.appendAssumeCapacity(MediaQuery{ + .qualifier = null, + .media_type = .all, + .condition = MediaCondition{ + .feature = MediaFeature{ + .plain = .{ + .name = .{ .standard = MediaFeatureId.@"prefers-color-scheme" }, + .value = .{ .ident = .{ .v = "dark " } }, + }, + }, + }, + }); + + break :brk list; + }, + }, + .rules = brk: { + var list: css.CssRuleList(T) = .{}; + + list.v.append(this.allocator, css.CssRule(T){ + .style = css.StyleRule(T){ + .selectors = style_rule.selectors.deepClone(this.allocator), + .vendor_prefix = css.VendorPrefix{ .none = true }, + .declarations = css.DeclarationBlock{ + .declarations = css.deepClone(css.Property, this.allocator, &this.dark), + .important_declarations = .{}, + }, + .rules = .{}, + .loc = style_rule.loc, + }, + }) catch bun.outOfMemory(); + + break :brk list; + }, + .loc = style_rule.loc, + }, + }) catch bun.outOfMemory(); + } + + return dest; + } + pub fn getAdditionalRulesHelper( + this: *const @This(), + comptime T: type, + comptime dir: []const u8, + comptime decls: []const u8, + sty: *const css.StyleRule(T), + dest: *ArrayList(css.CssRule(T)), + ) void { + var selectors = sty.selectors.deepClone(this.allocator); + for (selectors.v.slice_mut()) |*selector| { + selector.append(this.allocator, css.Component{ + .non_ts_pseudo_class = css.PseudoClass{ + .dir = .{ .direction = @field(css.selector.parser.Direction, dir) }, + }, + }); + + const rule = css.StyleRule(T){ + .selectors = selectors, + .vendor_prefix = css.VendorPrefix{ .none = true }, + .declarations = css.DeclarationBlock{ + .declarations = css.deepClone(css.Property, this.allocator, &@field(this, decls)), + .important_declarations = .{}, + }, + .rules = .{}, + .loc = sty.loc, + }; + + dest.append(this.allocator, .{ .style = rule }) catch bun.outOfMemory(); + } + } + + pub fn reset(this: *@This()) void { + for (this.supports.items) |*supp| { + supp.deinit(this.allocator); + } + this.supports.clearRetainingCapacity(); + + for (this.ltr.items) |*ltr| { + ltr.deinit(this.allocator); + } + this.ltr.clearRetainingCapacity(); + + for (this.rtl.items) |*rtl| { + rtl.deinit(this.allocator); + } + this.rtl.clearRetainingCapacity(); + + for (this.dark.items) |*dark| { + dark.deinit(this.allocator); + } + this.dark.clearRetainingCapacity(); + } }; diff --git a/src/css/css_modules.zig b/src/css/css_modules.zig index 941d698092..a0b84ed523 100644 --- a/src/css/css_modules.zig +++ b/src/css/css_modules.zig @@ -46,7 +46,7 @@ pub const CssModule = struct { allocator, "{s}", .{source}, - config.pattern.segments.items[0] == .hash, + config.pattern.segments.at(0).* == .hash, )); } break :hashes hashes; @@ -90,12 +90,12 @@ pub const CssModule = struct { composes: *const css.css_properties.css_modules.Composes, source_index: u32, ) css.Maybe(void, css.PrinterErrorKind) { - for (selectors.v.items) |*sel| { + for (selectors.v.slice()) |*sel| { if (sel.len() == 1) { const component: *const css.selector.parser.Component = &sel.components.items[0]; switch (component.*) { .class => |id| { - for (composes.names.items) |name| { + for (composes.names.slice()) |name| { const reference: CssModuleReference = if (composes.from) |*specifier| switch (specifier.*) { .source_index => |dep_source_index| { @@ -231,7 +231,7 @@ pub const Pattern = struct { closure: anytype, comptime writefn: *const fn (@TypeOf(closure), []const u8, replace_dots: bool) void, ) void { - for (this.segments.items) |*segment| { + for (this.segments.slice()) |*segment| { switch (segment.*) { .literal => |s| { writefn(closure, s, false); diff --git a/src/css/css_parser.zig b/src/css/css_parser.zig index b92cc4bcb2..6ee9b60a97 100644 --- a/src/css/css_parser.zig +++ b/src/css/css_parser.zig @@ -31,6 +31,7 @@ pub const UnknownAtRule = css_rules.unknown.UnknownAtRule; pub const ImportRule = css_rules.import.ImportRule; pub const StyleRule = css_rules.style.StyleRule; pub const StyleContext = css_rules.StyleContext; +pub const SupportsRule = css_rules.supports.SupportsRule; pub const MinifyContext = css_rules.MinifyContext; @@ -69,6 +70,10 @@ pub const DeclarationBlock = css_decls.DeclarationBlock; pub const selector = @import("./selectors/selector.zig"); pub const SelectorList = selector.parser.SelectorList; +pub const Selector = selector.parser.Selector; +pub const Component = selector.parser.Component; +pub const PseudoClass = selector.parser.PseudoClass; +pub const PseudoElement = selector.parser.PseudoElement; pub const logical = @import("./logical.zig"); pub const PropertyCategory = logical.PropertyCategory; @@ -99,6 +104,10 @@ pub const BasicParseErrorKind = errors_.BasicParseErrorKind; pub const SelectorError = errors_.SelectorError; pub const MinifyErrorKind = errors_.MinifyErrorKind; pub const MinifyError = errors_.MinifyError; +pub const MinifyErr = errors_.MinifyErr; + +pub const generic = @import("./generics.zig"); +pub const HASH_SEED = generic.HASH_SEED; pub const ImportConditions = css_rules.import.ImportConditions; @@ -117,12 +126,7 @@ pub fn OOM(e: anyerror) noreturn { bun.outOfMemory(); } -// TODO: smallvec -pub fn SmallList(comptime T: type, comptime N: comptime_int) type { - _ = N; // autofix - return ArrayList(T); -} - +pub const SmallList = @import("./small_list.zig").SmallList; pub const Bitflags = bun.Bitflags; pub const todo_stuff = struct { @@ -254,6 +258,7 @@ pub fn DefineListShorthand(comptime T: type) type { } pub fn DefineShorthand(comptime T: type, comptime property_name: PropertyIdTag) type { + _ = property_name; // autofix // TODO: validate map, make sure each field is set // make sure each field is same index as in T _ = T.PropertyFieldMap; @@ -261,172 +266,187 @@ pub fn DefineShorthand(comptime T: type, comptime property_name: PropertyIdTag) return struct { /// Returns a shorthand from the longhand properties defined in the given declaration block. pub fn fromLonghands(allocator: Allocator, decls: *const DeclarationBlock, vendor_prefix: VendorPrefix) ?struct { T, bool } { - var count: usize = 0; - var important_count: usize = 0; - var this: T = undefined; - var set_fields = std.StaticBitSet(std.meta.fields(T).len).initEmpty(); - const all_fields_set = std.StaticBitSet(std.meta.fields(T).len).initFull(); + _ = allocator; // autofix + _ = decls; // autofix + _ = vendor_prefix; // autofix + // var count: usize = 0; + // var important_count: usize = 0; + // var this: T = undefined; + // var set_fields = std.StaticBitSet(std.meta.fields(T).len).initEmpty(); + // const all_fields_set = std.StaticBitSet(std.meta.fields(T).len).initFull(); - // Loop through each property in `decls.declarations` and then `decls.important_declarations` - // The inline for loop is so we can share the code for both - const DECL_FIELDS = &.{ "declarations", "important_declarations" }; - inline for (DECL_FIELDS) |decl_field_name| { - const decl_list: *const ArrayList(css_properties.Property) = &@field(decls, decl_field_name); - const important = comptime std.mem.eql(u8, decl_field_name, "important_declarations"); + // // Loop through each property in `decls.declarations` and then `decls.important_declarations` + // // The inline for loop is so we can share the code for both + // const DECL_FIELDS = &.{ "declarations", "important_declarations" }; + // inline for (DECL_FIELDS) |decl_field_name| { + // const decl_list: *const ArrayList(css_properties.Property) = &@field(decls, decl_field_name); + // const important = comptime std.mem.eql(u8, decl_field_name, "important_declarations"); - // Now loop through each property in the list - main_loop: for (decl_list.items) |*property| { - // The property field map maps each field in `T` to a tag of `Property` - // Here we do `inline for` to basically switch on the tag of `property` to see - // if it matches a field in `T` which maps to the same tag - // - // Basically, check that `@as(PropertyIdTag, property.*)` equals `T.PropertyFieldMap[field.name]` - inline for (std.meta.fields(@TypeOf(T.PropertyFieldMap))) |field| { - const tag: PropertyIdTag = @as(?*const PropertyIdTag, field.default_value).?.*; + // // Now loop through each property in the list + // main_loop: for (decl_list.items) |*property| { + // // The property field map maps each field in `T` to a tag of `Property` + // // Here we do `inline for` to basically switch on the tag of `property` to see + // // if it matches a field in `T` which maps to the same tag + // // + // // Basically, check that `@as(PropertyIdTag, property.*)` equals `T.PropertyFieldMap[field.name]` + // inline for (std.meta.fields(@TypeOf(T.PropertyFieldMap))) |field| { + // const tag: PropertyIdTag = @as(?*const PropertyIdTag, field.default_value).?.*; - if (@intFromEnum(@as(PropertyIdTag, property.*)) == tag) { - if (@hasField(T.VendorPrefixMap, field.name)) { - if (@hasField(T.VendorPrefixMap, field.name) and - !VendorPrefix.eq(@field(property, field.name)[1], vendor_prefix)) - { - return null; - } + // if (@intFromEnum(@as(PropertyIdTag, property.*)) == tag) { + // if (@hasField(T.VendorPrefixMap, field.name)) { + // if (@hasField(T.VendorPrefixMap, field.name) and + // !VendorPrefix.eq(@field(property, field.name)[1], vendor_prefix)) + // { + // return null; + // } - @field(this, field.name) = if (@hasDecl(@TypeOf(@field(property, field.name)[0]), "clone")) - @field(property, field.name)[0].deepClone(allocator) - else - @field(property, field.name)[0]; - } else { - @field(this, field.name) = if (@hasDecl(@TypeOf(@field(property, field.name)), "clone")) - @field(property, field.name).deepClone(allocator) - else - @field(property, field.name); - } + // @field(this, field.name) = if (@hasDecl(@TypeOf(@field(property, field.name)[0]), "clone")) + // @field(property, field.name)[0].deepClone(allocator) + // else + // @field(property, field.name)[0]; + // } else { + // @field(this, field.name) = if (@hasDecl(@TypeOf(@field(property, field.name)), "clone")) + // @field(property, field.name).deepClone(allocator) + // else + // @field(property, field.name); + // } - set_fields.set(std.meta.fieldIndex(T, field.name)); - count += 1; - if (important) { - important_count += 1; - } + // set_fields.set(std.meta.fieldIndex(T, field.name)); + // count += 1; + // if (important) { + // important_count += 1; + // } - continue :main_loop; - } - } + // continue :main_loop; + // } + // } - // If `property` matches none of the tags in `T.PropertyFieldMap` then let's try - // if it matches the tag specified by `property_name` - if (@as(PropertyIdTag, property.*) == property_name) { - inline for (std.meta.fields(@TypeOf(T.PropertyFieldMap))) |field| { - if (@hasField(T.VendorPrefixMap, field.name)) { - @field(this, field.name) = if (@hasDecl(@TypeOf(@field(property, field.name)[0]), "clone")) - @field(property, field.name)[0].deepClone(allocator) - else - @field(property, field.name)[0]; - } else { - @field(this, field.name) = if (@hasDecl(@TypeOf(@field(property, field.name)), "clone")) - @field(property, field.name).deepClone(allocator) - else - @field(property, field.name); - } + // // If `property` matches none of the tags in `T.PropertyFieldMap` then let's try + // // if it matches the tag specified by `property_name` + // if (@as(PropertyIdTag, property.*) == property_name) { + // inline for (std.meta.fields(@TypeOf(T.PropertyFieldMap))) |field| { + // if (@hasField(T.VendorPrefixMap, field.name)) { + // @field(this, field.name) = if (@hasDecl(@TypeOf(@field(property, field.name)[0]), "clone")) + // @field(property, field.name)[0].deepClone(allocator) + // else + // @field(property, field.name)[0]; + // } else { + // @field(this, field.name) = if (@hasDecl(@TypeOf(@field(property, field.name)), "clone")) + // @field(property, field.name).deepClone(allocator) + // else + // @field(property, field.name); + // } - set_fields.set(std.meta.fieldIndex(T, field.name)); - count += 1; - if (important) { - important_count += 1; - } - } - continue :main_loop; - } + // set_fields.set(std.meta.fieldIndex(T, field.name)); + // count += 1; + // if (important) { + // important_count += 1; + // } + // } + // continue :main_loop; + // } - // Otherwise, try to convert to te fields using `.longhand()` - inline for (std.meta.fields(@TypeOf(T.PropertyFieldMap))) |field| { - const property_id = @unionInit( - PropertyId, - field.name, - if (@hasDecl(T.VendorPrefixMap, field.name)) vendor_prefix else {}, - ); - const value = property.longhand(&property_id); - if (@as(PropertyIdTag, value) == @as(PropertyIdTag, property_id)) { - @field(this, field.name) = if (@hasDecl(T.VendorPrefixMap, field.name)) - @field(value, field.name)[0] - else - @field(value, field.name); - set_fields.set(std.meta.fieldIndex(T, field.name)); - count += 1; - if (important) { - important_count += 1; - } - } - } - } - } + // // Otherwise, try to convert to te fields using `.longhand()` + // inline for (std.meta.fields(@TypeOf(T.PropertyFieldMap))) |field| { + // const property_id = @unionInit( + // PropertyId, + // field.name, + // if (@hasDecl(T.VendorPrefixMap, field.name)) vendor_prefix else {}, + // ); + // const value = property.longhand(&property_id); + // if (@as(PropertyIdTag, value) == @as(PropertyIdTag, property_id)) { + // @field(this, field.name) = if (@hasDecl(T.VendorPrefixMap, field.name)) + // @field(value, field.name)[0] + // else + // @field(value, field.name); + // set_fields.set(std.meta.fieldIndex(T, field.name)); + // count += 1; + // if (important) { + // important_count += 1; + // } + // } + // } + // } + // } - if (important_count > 0 and important_count != count) { - return null; - } + // if (important_count > 0 and important_count != count) { + // return null; + // } - // All properties in the group must have a matching value to produce a shorthand. - if (set_fields.eql(all_fields_set)) { - return .{ this, important_count > 0 }; - } + // // All properties in the group must have a matching value to produce a shorthand. + // if (set_fields.eql(all_fields_set)) { + // return .{ this, important_count > 0 }; + // } - return null; + // return null; + @panic(todo_stuff.depth); } /// Returns a shorthand from the longhand properties defined in the given declaration block. pub fn longhands(vendor_prefix: VendorPrefix) []const PropertyId { - const out: []const PropertyId = comptime out: { - var out: [std.meta.fields(@TypeOf(T.PropertyFieldMap)).len]PropertyId = undefined; + _ = vendor_prefix; // autofix + // const out: []const PropertyId = comptime out: { + // var out: [std.meta.fields(@TypeOf(T.PropertyFieldMap)).len]PropertyId = undefined; - for (std.meta.fields(@TypeOf(T.PropertyFieldMap)), 0..) |field, i| { - out[i] = @unionInit( - PropertyId, - field.name, - if (@hasField(T.VendorPrefixMap, field.name)) vendor_prefix else {}, - ); - } + // for (std.meta.fields(@TypeOf(T.PropertyFieldMap)), 0..) |field, i| { + // out[i] = @unionInit( + // PropertyId, + // field.name, + // if (@hasField(T.VendorPrefixMap, field.name)) vendor_prefix else {}, + // ); + // } - break :out out; - }; - return out; + // break :out out; + // }; + // return out; + + @panic(todo_stuff.depth); } /// Returns a longhand property for this shorthand. pub fn longhand(this: *const T, allocator: Allocator, property_id: *const PropertyId) ?Property { - inline for (std.meta.fields(@TypeOf(T.PropertyFieldMap))) |field| { - if (@as(PropertyIdTag, property_id.*) == @field(T.PropertyFieldMap, field.name)) { - const val = if (@hasDecl(@TypeOf(@field(T, field.namee)), "clone")) - @field(this, field.name).deepClone(allocator) - else - @field(this, field.name); - return @unionInit( - Property, - field.name, - if (@field(T.VendorPrefixMap, field.name)) - .{ val, @field(property_id, field.name)[1] } - else - val, - ); - } - } - return null; + _ = this; // autofix + _ = allocator; // autofix + _ = property_id; // autofix + // inline for (std.meta.fields(@TypeOf(T.PropertyFieldMap))) |field| { + // if (@as(PropertyIdTag, property_id.*) == @field(T.PropertyFieldMap, field.name)) { + // const val = if (@hasDecl(@TypeOf(@field(T, field.namee)), "clone")) + // @field(this, field.name).deepClone(allocator) + // else + // @field(this, field.name); + // return @unionInit( + // Property, + // field.name, + // if (@field(T.VendorPrefixMap, field.name)) + // .{ val, @field(property_id, field.name)[1] } + // else + // val, + // ); + // } + // } + // return null; + @panic(todo_stuff.depth); } /// Updates this shorthand from a longhand property. pub fn setLonghand(this: *T, allocator: Allocator, property: *const Property) bool { - inline for (std.meta.fields(T.PropertyFieldMap)) |field| { - if (@as(PropertyIdTag, property.*) == @field(T.PropertyFieldMap, field.name)) { - const val = if (@hasDecl(@TypeOf(@field(T, field.name)), "clone")) - @field(this, field.name).deepClone(allocator) - else - @field(this, field.name); + _ = this; // autofix + _ = allocator; // autofix + _ = property; // autofix + // inline for (std.meta.fields(T.PropertyFieldMap)) |field| { + // if (@as(PropertyIdTag, property.*) == @field(T.PropertyFieldMap, field.name)) { + // const val = if (@hasDecl(@TypeOf(@field(T, field.name)), "clone")) + // @field(this, field.name).deepClone(allocator) + // else + // @field(this, field.name); - @field(this, field.name) = val; + // @field(this, field.name) = val; - return true; - } - } - return false; + // return true; + // } + // } + // return false; + @panic(todo_stuff.depth); } }; } @@ -462,9 +482,18 @@ pub fn DefineRectShorthand(comptime T: type, comptime V: type) type { } pub fn DefineSizeShorthand(comptime T: type, comptime V: type) type { - const fields = std.meta.fields(T); - if (fields.len != 2) @compileError("DefineSizeShorthand must be used on a struct with 2 fields"); + if (std.meta.fields(T).len != 2) @compileError("DefineSizeShorthand must be used on a struct with 2 fields"); return struct { + pub fn toCss(this: *const T, comptime W: type, dest: *Printer(W)) PrintErr!void { + const size: css_values.size.Size2D(V) = .{ + .a = @field(this, std.meta.fields(T)[0].name), + .b = @field(this, std.meta.fields(T)[1].name), + }; + return size.toCss(W, dest); + // TODO: unfuck this + // @panic(todo_stuff.depth); + } + pub fn parse(input: *Parser) Result(T) { const size = switch (css_values.size.Size2D(V).parse(input)) { .result => |v| v, @@ -472,18 +501,12 @@ pub fn DefineSizeShorthand(comptime T: type, comptime V: type) type { }; var this: T = undefined; - @field(this, fields[0].name) = size.a; - @field(this, fields[1].name) = size.b; + @field(this, std.meta.fields(T)[0].name) = size.a; + @field(this, std.meta.fields(T)[1].name) = size.b; return .{ .result = this }; - } - - pub fn toCss(this: *const T, comptime W: type, dest: *Printer(W)) PrintErr!void { - const size: css_values.size.Size2D(V) = .{ - .a = @field(this, fields[0].name), - .b = @field(this, fields[1].name), - }; - return size.toCss(W, dest); + // TODO: unfuck this + // @panic(todo_stuff.depth); } }; } @@ -496,8 +519,83 @@ pub fn DeriveParse(comptime T: type) type { const Map = bun.ComptimeEnumMap(enum_actual_type); - // TODO: this has to work for enums and union(enums) return struct { + pub fn parse(input: *Parser) Result(T) { + if (comptime is_union_enum) { + const payload_count, const first_payload_index, const void_count, const first_void_index = comptime counts: { + var first_void_index: ?usize = null; + var first_payload_index: ?usize = null; + var payload_count: usize = 0; + var void_count: usize = 0; + for (tyinfo.Union.fields, 0..) |field, i| { + if (field.type == void) { + void_count += 1; + if (first_void_index == null) first_void_index = i; + } else { + payload_count += 1; + if (first_payload_index == null) first_payload_index = i; + } + } + if (first_payload_index == null) { + @compileError("Type defined as `union(enum)` but no variant carries a payload. Make it an `enum` instead."); + } + if (first_void_index) |void_index| { + // Check if they overlap + if (first_payload_index.? < void_index and void_index < first_payload_index.? + payload_count) @compileError("Please put all the fields with data together and all the fields with no data together."); + if (first_payload_index.? > void_index and first_payload_index.? < void_index + void_count) @compileError("Please put all the fields with data together and all the fields with no data together."); + } + break :counts .{ payload_count, first_payload_index.?, void_count, first_void_index }; + }; + + return gnerateCode(input, first_payload_index, first_void_index, void_count, payload_count); + } + + const location = input.currentSourceLocation(); + const ident = switch (input.expectIdent()) { + .result => |v| v, + .err => |e| return .{ .err = e }, + }; + if (Map.getCaseInsensitiveWithEql(ident, bun.strings.eqlComptimeIgnoreLen)) |matched| { + inline for (bun.meta.EnumFields(enum_type)) |field| { + if (field.value == @intFromEnum(matched)) { + if (comptime is_union_enum) return .{ .result = @unionInit(T, field.name, void) }; + return .{ .result = @enumFromInt(field.value) }; + } + } + unreachable; + } + return .{ .err = location.newUnexpectedTokenError(.{ .ident = ident }) }; + } + + /// Comptime code which constructs the parsing code for a union(enum) which could contain + /// void fields (fields with no associated data) and payload fields (fields which carry data), + /// for example: + /// + /// ```zig + /// /// A value for the [border-width](https://www.w3.org/TR/css-backgrounds-3/#border-width) property. + /// pub const BorderSideWidth = union(enum) { + /// /// A UA defined `thin` value. + /// thin, + /// /// A UA defined `medium` value. + /// medium, + /// /// A UA defined `thick` value. + /// thick, + /// /// An explicit width. + /// length: Length, + /// } + /// ``` + /// + /// During parsing, we can check if it is one of the void fields (in this case `thin`, `medium`, or `thick`) by reading a single + /// identifier from the Parser, and checking if it matches any of the void field names. We already constructed a ComptimeEnumMap (see above) + /// to make this super cheap. + /// + /// If we don't get an identifier that matches any of the void fields, we can then try to parse the payload fields. + /// + /// This function is made more complicated by the fact that it tries to parse in order of the fields that were declared in the union(enum). + /// If, for example, all the void fields were declared after the `length: Length` field, this function will try to parse the `length` field first, + /// and then try to parse the void fields. + /// + /// This parsing order is a detail copied from LightningCSS. I'm not sure if it is necessary. But it could be. inline fn gnerateCode( input: *Parser, comptime first_payload_index: usize, @@ -642,53 +740,6 @@ pub fn DeriveParse(comptime T: type) type { // unreachable; // } - pub fn parse(input: *Parser) Result(T) { - if (comptime is_union_enum) { - const payload_count, const first_payload_index, const void_count, const first_void_index = comptime counts: { - var first_void_index: ?usize = null; - var first_payload_index: ?usize = null; - var payload_count: usize = 0; - var void_count: usize = 0; - for (tyinfo.Union.fields, 0..) |field, i| { - if (field.type == void) { - void_count += 1; - if (first_void_index == null) first_void_index = i; - } else { - payload_count += 1; - if (first_payload_index == null) first_payload_index = i; - } - } - if (first_payload_index == null) { - @compileError("Type defined as `union(enum)` but no variant carries a payload. Make it an `enum` instead."); - } - if (first_void_index) |void_index| { - // Check if they overlap - if (first_payload_index.? < void_index and void_index < first_payload_index.? + payload_count) @compileError("Please put all the fields with data together and all the fields with no data together."); - if (first_payload_index.? > void_index and first_payload_index.? < void_index + void_count) @compileError("Please put all the fields with data together and all the fields with no data together."); - } - break :counts .{ payload_count, first_payload_index.?, void_count, first_void_index }; - }; - - return gnerateCode(input, first_payload_index, first_void_index, void_count, payload_count); - } - - const location = input.currentSourceLocation(); - const ident = switch (input.expectIdent()) { - .result => |v| v, - .err => |e| return .{ .err = e }, - }; - if (Map.getCaseInsensitiveWithEql(ident, bun.strings.eqlComptimeIgnoreLen)) |matched| { - inline for (bun.meta.EnumFields(enum_type)) |field| { - if (field.value == @intFromEnum(matched)) { - if (comptime is_union_enum) return .{ .result = @unionInit(T, field.name, void) }; - return .{ .result = @enumFromInt(field.value) }; - } - } - unreachable; - } - return .{ .err = location.newUnexpectedTokenError(.{ .ident = ident }) }; - } - // pub fn parse(this: *const T, comptime W: type, dest: *Printer(W)) PrintErr!void { // // to implement this, we need to cargo expand the derive macro // _ = this; // autofix @@ -698,38 +749,57 @@ pub fn DeriveParse(comptime T: type) type { }; } +/// This uses comptime reflection to generate a `toCss` function enums and union(enum)s. +/// +/// Supported payload types for union(enum)s are: +/// - any type that has a `toCss` function +/// - void types (stringifies the identifier) +/// - optional types (unwraps the optional) +/// - anonymous structs, will automatically serialize it if it has a `__generateToCss` function pub fn DeriveToCss(comptime T: type) type { + const tyinfo = @typeInfo(T); const enum_fields = bun.meta.EnumFields(T); - // TODO: this has to work for enums and union(enums) + const is_enum_or_union_enum = tyinfo == .Union or tyinfo == .Enum; + return struct { pub fn toCss(this: *const T, comptime W: type, dest: *Printer(W)) PrintErr!void { - inline for (std.meta.fields(T), 0..) |field, i| { - if (@intFromEnum(this.*) == enum_fields[i].value) { - if (comptime field.type == void) { - return dest.writeStr(enum_fields[i].name); - } else if (comptime generic.hasToCss(T)) { - return generic.toCss(field.type, &@field(this, field.name), W, dest); - } else { - const variant_fields = std.meta.fields(field.type); - if (variant_fields.len > 1) { - var optional_count = 0; - inline for (variant_fields) |variant_field| { - if (@typeInfo(variant_field.type) == .Optional) { - optional_count += 1; - if (optional_count > 1) @compileError("Not supported for multiple optional fields yet sorry."); - if (@field(@field(this, field.name), variant_field.name)) |*value| { - try generic.toCss(@TypeOf(value.*), W, dest); + if (comptime is_enum_or_union_enum) { + inline for (std.meta.fields(T), 0..) |field, i| { + if (@intFromEnum(this.*) == enum_fields[i].value) { + if (comptime field.type == void) { + return dest.writeStr(enum_fields[i].name); + } else if (comptime generic.hasToCss(field.type)) { + return generic.toCss(field.type, &@field(this, field.name), W, dest); + } else if (@hasDecl(field.type, "__generateToCss") and @typeInfo(field.type) == .Struct) { + const variant_fields = std.meta.fields(field.type); + if (variant_fields.len > 1) { + const last = variant_fields.len - 1; + inline for (variant_fields, 0..) |variant_field, j| { + // Unwrap it from the optional + if (@typeInfo(variant_field.type) == .Optional) { + if (@field(@field(this, field.name), variant_field.name)) |*value| { + try value.toCss(W, dest); + } + } else { + try @field(@field(this, field.name), variant_field.name).toCss(W, dest); + } + + // Emit a space if there are more fields after + if (comptime j != last) { + try dest.writeChar(' '); } - } else { - try @field(@field(this, field.name), variant_field.name).toCss(W, dest); } + } else { + const variant_field = variant_fields[0]; + try @field(variant_field.type, "toCss")(@field(@field(this, field.name), variant_field.name), W, dest); } } else { - const variant_field = variant_fields[0]; - try @field(variant_field.type, "toCss")(@field(@field(this, field.name), variant_field.name), W, dest); + @compileError("Don't know how to serialize this variant: " ++ @typeName(field.type) ++ ", on " ++ @typeName(T) ++ ".\n\nYou probably want to implement a `toCss` function for this type, or add a dummy `fn __generateToCss() void {}` to the type signal that it is okay for it to be auto-generated by this function.."); } } } + } else { + @compileError("Unsupported type: " ++ @typeName(T)); } return; } @@ -769,6 +839,10 @@ pub fn DefineEnumProperty(comptime T: type) type { const fields: []const std.builtin.Type.EnumField = std.meta.fields(T); return struct { + pub fn eql(lhs: *const T, rhs: *const T) bool { + return @intFromEnum(lhs.*) == @intFromEnum(rhs.*); + } + pub fn asStr(this: *const T) []const u8 { const tag = @intFromEnum(this.*); inline for (fields) |field| { @@ -796,6 +870,15 @@ pub fn DefineEnumProperty(comptime T: type) type { pub fn toCss(this: *const T, comptime W: type, dest: *Printer(W)) PrintErr!void { return dest.writeStr(asStr(this)); } + + pub inline fn deepClone(this: *const T, _: std.mem.Allocator) T { + return this.*; + } + + pub fn hash(this: *const T, hasher: *std.hash.Wyhash) void { + const tag = @intFromEnum(this.*); + hasher.update(std.mem.asBytes(&tag)); + } }; } @@ -1170,10 +1253,13 @@ pub fn ValidQualifiedRuleParser(comptime T: type) void { } pub const DefaultAtRule = struct { - pub fn toCss(this: *const @This(), comptime W: type, dest: *Printer(W)) PrintErr!void { - _ = this; // autofix + pub fn toCss(_: *const @This(), comptime W: type, dest: *Printer(W)) PrintErr!void { return dest.newError(.fmt_error, null); } + + pub fn deepClone(_: *const @This(), _: std.mem.Allocator) @This() { + return .{}; + } }; pub const DefaultAtRuleParser = struct { @@ -2515,51 +2601,53 @@ pub fn StyleSheet(comptime AtRule: type) type { const This = @This(); + pub fn empty(allocator: Allocator) This { + return This{ + .rules = .{}, + .sources = .{}, + .source_map_urls = .{}, + .license_comments = .{}, + .options = ParserOptions.default(allocator, null), + }; + } + /// Minify and transform the style sheet for the provided browser targets. pub fn minify(this: *@This(), allocator: Allocator, options: MinifyOptions) Maybe(void, Err(MinifyErrorKind)) { - _ = this; // autofix - _ = allocator; // autofix - _ = options; // autofix - // TODO + const ctx = PropertyHandlerContext.new(allocator, options.targets, &options.unused_symbols); + var handler = declaration.DeclarationHandler.default(); + var important_handler = declaration.DeclarationHandler.default(); + + // @custom-media rules may be defined after they are referenced, but may only be defined at the top level + // of a stylesheet. Do a pre-scan here and create a lookup table by name. + var custom_media: ?std.StringArrayHashMapUnmanaged(css_rules.custom_media.CustomMediaRule) = if (this.options.flags.contains(ParserFlags{ .custom_media = true }) and options.targets.shouldCompileSame(.custom_media_queries)) brk: { + var custom_media = std.StringArrayHashMapUnmanaged(css_rules.custom_media.CustomMediaRule){}; + + for (this.rules.v.items) |*rule| { + if (rule.* == .custom_media) { + custom_media.put(allocator, rule.custom_media.name.v, rule.custom_media.deepClone(allocator)) catch bun.outOfMemory(); + } + } + + break :brk custom_media; + } else null; + defer if (custom_media) |*media| media.deinit(allocator); + + var minify_ctx = MinifyContext{ + .allocator = allocator, + .targets = &options.targets, + .handler = &handler, + .important_handler = &important_handler, + .handler_context = ctx, + .unused_symbols = &options.unused_symbols, + .custom_media = custom_media, + .css_modules = this.options.css_modules != null, + }; + + this.rules.minify(&minify_ctx, false) catch { + @panic("TODO: Handle"); + }; + return .{ .result = {} }; - - // const ctx = PropertyHandlerContext.new(allocator, options.targets, &options.unused_symbols); - // var handler = declaration.DeclarationHandler.default(); - // var important_handler = declaration.DeclarationHandler.default(); - - // // @custom-media rules may be defined after they are referenced, but may only be defined at the top level - // // of a stylesheet. Do a pre-scan here and create a lookup table by name. - // const custom_media: ?std.StringArrayHashMapUnmanaged(css_rules.custom_media.CustomMediaRule) = if (this.options.flags.contains(ParserFlags{ .custom_media = true }) and options.targets.shouldCompileSame(.custom_media_queries)) brk: { - // var custom_media = std.StringArrayHashMapUnmanaged(css_rules.custom_media.CustomMediaRule){}; - - // for (this.rules.v.items) |*rule| { - // if (rule.* == .custom_media) { - // custom_media.put(allocator, rule.custom_media.name, rule.deepClone(allocator)) catch bun.outOfMemory(); - // } - // } - - // break :brk custom_media; - // } else null; - // defer if (custom_media) |media| media.deinit(allocator); - - // var minify_ctx = MinifyContext{ - // .targets = &options.targets, - // .handler = &handler, - // .important_handler = &important_handler, - // .handler_context = ctx, - // .unused_symbols = &options.unused_symbols, - // .custom_media = custom_media, - // .css_modules = this.options.css_modules != null, - // }; - - // switch (this.rules.minify(&minify_ctx, false)) { - // .result => return .{ .result = {} }, - // .err => |e| { - // _ = e; // autofix - // @panic("TODO: here"); - // // return .{ .err = .{ .kind = e, .loc = } }; - // }, - // } } pub fn toCssWithWriter(this: *const @This(), allocator: Allocator, writer: anytype, options: css_printer.PrinterOptions, import_records: ?*const bun.BabyList(ImportRecord)) PrintErr!ToCssResultInternal { @@ -2579,7 +2667,7 @@ pub fn StyleSheet(comptime AtRule: type) type { for (this.license_comments.items) |comment| { try printer.writeStr("/*"); - try printer.writeStr(comment); + try printer.writeComment(comment); try printer.writeStr("*/"); try printer.newline(); } @@ -3003,6 +3091,7 @@ pub const Parser = struct { stop_before: Delimiters = Delimiters.NONE, import_records: ?*bun.BabyList(ImportRecord), + // TODO: dedupe import records?? pub fn addImportRecordForUrl(this: *Parser, url: []const u8, start_position: usize) Result(u32) { if (this.import_records) |import_records| { const idx = import_records.len; @@ -5200,6 +5289,12 @@ pub const Token = union(TokenKind) { has_sign: bool, unit_value: f32, int_value: ?i32, + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return implementEql(@This(), lhs, rhs); + } + + pub fn __generateHash() void {} }, dimension: Dimension, @@ -5247,6 +5342,14 @@ pub const Token = union(TokenKind) { /// Not an actual token in the spec, but we keep it anyway comment: []const u8, + pub fn eql(lhs: *const Token, rhs: *const Token) bool { + return implementEql(Token, lhs, rhs); + } + + pub fn hash(this: *const @This(), hasher: *std.hash.Wyhash) void { + return implementHash(@This(), this, hasher); + } + /// Return whether this token represents a parse error. /// /// `BadUrl` and `BadString` are tokenizer-level parse errors. @@ -5501,12 +5604,28 @@ const Num = struct { has_sign: bool, value: f32, int_value: ?i32, + + pub fn eql(lhs: *const Num, rhs: *const Num) bool { + return implementEql(Num, lhs, rhs); + } + + pub fn hash(this: *const @This(), hasher: *std.hash.Wyhash) void { + return implementHash(@This(), this, hasher); + } }; const Dimension = struct { num: Num, /// e.g. "px" unit: []const u8, + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return implementEql(@This(), lhs, rhs); + } + + pub fn hash(this: *const @This(), hasher: *std.hash.Wyhash) void { + return implementHash(@This(), this, hasher); + } }; const CopyOnWriteStr = union(enum) { @@ -6016,166 +6135,170 @@ pub const serializer = struct { } }; -pub const generic = struct { - pub inline fn parseWithOptions(comptime T: type, input: *Parser, options: *const ParserOptions) Result(T) { - if (@hasDecl(T, "parseWithOptions")) return T.parseWithOptions(input, options); - return switch (T) { - f32 => CSSNumberFns.parse(input), - CSSInteger => CSSIntegerFns.parse(input), - CustomIdent => CustomIdentFns.parse(input), - DashedIdent => DashedIdentFns.parse(input), - Ident => IdentFns.parse(input), - else => T.parse(input), +pub inline fn implementDeepClone(comptime T: type, this: *const T, allocator: Allocator) T { + const tyinfo = @typeInfo(T); + + if (comptime bun.meta.isSimpleCopyType(T)) { + return this.*; + } + + if (comptime bun.meta.looksLikeListContainerType(T)) |result| { + return switch (result) { + .array_list => deepClone(result.child, allocator, this), + .baby_list => @panic("Not implemented."), + .small_list => this.deepClone(allocator), }; } - pub inline fn parse(comptime T: type, input: *Parser) Result(T) { - return switch (T) { - f32 => CSSNumberFns.parse(input), - CSSInteger => CSSIntegerFns.parse(input), - CustomIdent => CustomIdentFns.parse(input), - DashedIdent => DashedIdentFns.parse(input), - Ident => IdentFns.parse(input), - else => T.parse(input), - }; + if (comptime T == []const u8) { + return this.*; } - pub inline fn parseFor(comptime T: type) @TypeOf(struct { - fn parsefn(input: *Parser) Result(T) { - return generic.parse(T, input); - } - }.parsefn) { - return struct { - fn parsefn(input: *Parser) Result(T) { - return generic.parse(T, input); + if (comptime @typeInfo(T) == .Pointer) { + const TT = std.meta.Child(T); + return implementEql(TT, this.*); + } + + return switch (tyinfo) { + .Struct => { + var strct: T = undefined; + inline for (tyinfo.Struct.fields) |field| { + @field(strct, field.name) = generic.deepClone(field.type, &@field(this, field.name), allocator); } - }.parsefn; - } + return strct; + }, + .Union => { + inline for (bun.meta.EnumFields(T), tyinfo.Union.fields) |enum_field, union_field| { + if (@intFromEnum(this.*) == enum_field.value) + return @unionInit(T, enum_field.name, generic.deepClone(union_field.type, &@field(this, enum_field.name), allocator)); + } + unreachable; + }, + else => @compileError("Unhandled type " ++ @typeName(T)), + }; +} - pub fn hasToCss(comptime T: type) bool { - return switch (T) { - f32 => true, - else => @hasDecl(T, "toCss"), - }; +/// A function to implement `lhs.eql(&rhs)` for the many types in the CSS parser that needs this. +/// +/// This is the equivalent of doing `#[derive(PartialEq])` in Rust. +/// +/// This function only works on simple types like: +/// - Simple equality types (e.g. integers, floats, strings, enums, etc.) +/// - Types which implement a `.eql(lhs: *const @This(), rhs: *const @This()) bool` function +/// +/// Or compound types composed of simple types such as: +/// - Pointers to simple types +/// - Optional simple types +/// - Structs, Arrays, and Unions +pub fn implementEql(comptime T: type, this: *const T, other: *const T) bool { + const tyinfo = @typeInfo(T); + if (comptime bun.meta.isSimpleEqlType(T)) { + return this.* == other.*; } + if (comptime T == []const u8) { + return bun.strings.eql(this.*, other.*); + } + if (comptime @typeInfo(T) == .Pointer) { + const TT = std.meta.Child(T); + return implementEql(TT, this.*, other.*); + } + if (comptime @typeInfo(T) == .Optional) { + const TT = std.meta.Child(T); + if (this.* != null and other.* != null) return implementEql(TT, &this.*.?, &other.*.?); + return false; + } + return switch (tyinfo) { + .Optional => @compileError("Handled above, this means Zack wrote a bug."), + .Pointer => @compileError("Handled above, this means Zack wrote a bug."), + .Array => { + const Child = std.meta.Child(T); + if (comptime bun.meta.isSimpleEqlType(Child)) { + return std.mem.eql(Child, &this.*, &other.*); + } + if (this.len != other.len) return false; + for (this.*, other.*) |a, b| { + if (!generic.eql(Child, &a, &b)) return false; + } + return true; + }, + .Struct => { + inline for (tyinfo.Struct.fields) |field| { + if (!generic.eql(field.type, &@field(this, field.name), &@field(other, field.name))) return false; + } + return true; + }, + .Union => { + if (tyinfo.Union.tag_type == null) @compileError("Unions must have a tag type"); + if (@intFromEnum(this.*) != @intFromEnum(other.*)) return false; + const enum_fields = bun.meta.EnumFields(T); + inline for (enum_fields, std.meta.fields(T)) |enum_field, union_field| { + if (enum_field.value == @intFromEnum(this.*)) { + if (union_field.type != void) { + return generic.eql(union_field.type, &@field(this, enum_field.name), &@field(other, enum_field.name)); + } else return true; + } + } + return true; + }, + else => @compileError("Unsupported type: " ++ @typeName(T)), + }; +} - pub inline fn toCss(comptime T: type, this: *const T, comptime W: type, dest: *Printer(W)) PrintErr!void { - if (@typeInfo(T) == .Pointer) { - const TT = std.meta.Child(T); - return toCss(TT, this.*, W, dest); - } - return switch (T) { - f32 => CSSNumberFns.toCss(this, W, dest), - CSSInteger => CSSIntegerFns.toCss(this, W, dest), - CustomIdent => CustomIdentFns.toCss(this, W, dest), - DashedIdent => DashedIdentFns.toCss(this, W, dest), - Ident => IdentFns.toCss(this, W, dest), - else => T.toCss(this, W, dest), - }; +pub fn implementHash(comptime T: type, this: *const T, hasher: *std.hash.Wyhash) void { + const tyinfo = @typeInfo(T); + if (comptime T == void) return; + if (comptime bun.meta.isSimpleEqlType(T)) { + return hasher.update(std.mem.asBytes(&this)); } - - pub fn eqlList(comptime T: type, lhs: *const ArrayList(T), rhs: *const ArrayList(T)) bool { - if (lhs.items.len != rhs.items.len) return false; - for (lhs.items, 0..) |*item, i| { - if (!eql(T, item, &rhs.items[i])) return false; - } - return true; + if (comptime T == []const u8) { + return hasher.update(this.*); } - - pub inline fn eql(comptime T: type, lhs: *const T, rhs: *const T) bool { - return switch (T) { - f32 => lhs.* == rhs.*, - CSSInteger => lhs.* == rhs.*, - CustomIdent, DashedIdent, Ident => bun.strings.eql(lhs.*, rhs.*), - else => T.eql(lhs, rhs), - }; + if (comptime @typeInfo(T) == .Pointer) { + @compileError("Invalid type for implementHash(): " ++ @typeName(T)); } - - const Angle = css_values.angle.Angle; - pub inline fn tryFromAngle(comptime T: type, angle: Angle) ?T { - return switch (T) { - CSSNumber => CSSNumberFns.tryFromAngle(angle), - Angle => return Angle.tryFromAngle(angle), - else => T.tryFromAngle(angle), - }; + if (comptime @typeInfo(T) == .Optional) { + @compileError("Invalid type for implementHash(): " ++ @typeName(T)); } - - pub inline fn trySign(comptime T: type, val: *const T) ?f32 { - return switch (T) { - CSSNumber => CSSNumberFns.sign(val), - else => { - if (@hasDecl(T, "sign")) return T.sign(val); - return T.trySign(val); - }, - }; - } - - pub inline fn tryMap( - comptime T: type, - val: *const T, - comptime map_fn: *const fn (a: f32) f32, - ) ?T { - return switch (T) { - CSSNumber => map_fn(val.*), - else => { - if (@hasDecl(T, "map")) return T.map(val, map_fn); - return T.tryMap(val, map_fn); - }, - }; - } - - pub inline fn tryOpTo( - comptime T: type, - comptime R: type, - lhs: *const T, - rhs: *const T, - ctx: anytype, - comptime op_fn: *const fn (@TypeOf(ctx), a: f32, b: f32) R, - ) ?R { - return switch (T) { - CSSNumber => op_fn(ctx, lhs.*, rhs.*), - else => { - if (@hasDecl(T, "opTo")) return T.opTo(lhs, rhs, R, ctx, op_fn); - return T.tryOpTo(lhs, rhs, R, ctx, op_fn); - }, - }; - } - - pub inline fn tryOp( - comptime T: type, - lhs: *const T, - rhs: *const T, - ctx: anytype, - comptime op_fn: *const fn (@TypeOf(ctx), a: f32, b: f32) f32, - ) ?T { - return switch (T) { - Angle => Angle.tryOp(lhs, rhs, ctx, op_fn), - CSSNumber => op_fn(ctx, lhs.*, rhs.*), - else => { - if (@hasDecl(T, "op")) return T.op(lhs, rhs, ctx, op_fn); - return T.tryOp(lhs, rhs, ctx, op_fn); - }, - }; - } - - pub inline fn partialCmp(comptime T: type, lhs: *const T, rhs: *const T) ?std.math.Order { - return switch (T) { - f32 => partialCmpF32(lhs, rhs), - CSSInteger => std.math.order(lhs.*, rhs.*), - css_values.angle.Angle => css_values.angle.Angle.partialCmp(lhs, rhs), - else => T.partialCmp(lhs, rhs), - }; - } - - pub inline fn partialCmpF32(lhs: *const f32, rhs: *const f32) ?std.math.Order { - const lte = lhs.* <= rhs.*; - const rte = lhs.* >= rhs.*; - if (!lte and !rte) return null; - if (!lte and rte) return .gt; - if (lte and !rte) return .lt; - return .eq; - } -}; + return switch (tyinfo) { + .Optional => unreachable, + .Pointer => unreachable, + .Array => { + if (comptime @typeInfo(T) == .Optional) { + @compileError("Invalid type for implementHash(): " ++ @typeName(T)); + } + }, + .Struct => { + inline for (tyinfo.Struct.fields) |field| { + if (comptime generic.hasHash(field.type)) { + generic.hash(field.type, &@field(this, field.name), hasher); + } else if (@hasDecl(field.type, "__generateHash") and @typeInfo(field.type) == .Struct) { + implementHash(field.type, &@field(this, field.name), hasher); + } else { + @compileError("Can't hash these fields: " ++ @typeName(field.type) ++ ". On " ++ @typeName(T)); + } + } + return; + }, + .Union => { + if (tyinfo.Union.tag_type == null) @compileError("Unions must have a tag type"); + const enum_fields = bun.meta.EnumFields(T); + inline for (enum_fields, std.meta.fields(T)) |enum_field, union_field| { + if (enum_field.value == @intFromEnum(this.*)) { + const field = union_field; + if (comptime generic.hasHash(field.type)) { + generic.hash(field.type, &@field(this, field.name), hasher); + } else if (@hasDecl(field.type, "__generateHash") and @typeInfo(field.type) == .Struct) { + implementHash(field.type, &@field(this, field.name), hasher); + } else { + @compileError("Can't hash these fields: " ++ @typeName(field.type) ++ ". On " ++ @typeName(T)); + } + } + } + return; + }, + else => @compileError("Unsupported type: " ++ @typeName(T)), + }; +} pub const parse_utility = struct { /// Parse a value from a string. @@ -6240,6 +6363,17 @@ pub const to_css = struct { return; } + pub fn fromBabyList(comptime T: type, this: *const bun.BabyList(T), comptime W: type, dest: *Printer(W)) PrintErr!void { + const len = this.len; + for (this.sliceConst(), 0..) |*val, idx| { + try val.toCss(W, dest); + if (idx < len - 1) { + try dest.delim(',', false); + } + } + return; + } + pub fn integer(comptime T: type, this: T, comptime W: type, dest: *Printer(W)) PrintErr!void { const MAX_LEN = comptime maxDigits(T); var buf: [MAX_LEN]u8 = undefined; @@ -6317,11 +6451,8 @@ pub inline fn copysign(self: f32, sign: f32) f32 { pub fn deepClone(comptime V: type, allocator: Allocator, list: *const ArrayList(V)) ArrayList(V) { var newlist = ArrayList(V).initCapacity(allocator, list.items.len) catch bun.outOfMemory(); - for (list.items) |item| { - newlist.appendAssumeCapacity(switch (V) { - i32, i64, u32, u64, f32, f64 => item, - else => item.deepClone(allocator), - }); + for (list.items) |*item| { + newlist.appendAssumeCapacity(generic.deepClone(V, item, allocator)); } return newlist; diff --git a/src/css/declaration.zig b/src/css/declaration.zig index 86ec09d67e..b04017d1d3 100644 --- a/src/css/declaration.zig +++ b/src/css/declaration.zig @@ -30,6 +30,10 @@ pub const DeclarationBlock = struct { const This = @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{}; @@ -113,6 +117,72 @@ pub const DeclarationBlock = struct { 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) { + hndlr.decls.append(ctx.allocator, prop.*) catch bun.outOfMemory(); + // 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 { @@ -230,7 +300,35 @@ pub fn parse_declaration( } pub const DeclarationHandler = struct { + direction: ?css.css_properties.text.Direction, + decls: DeclarationList, + + pub fn finalize(this: *DeclarationHandler, context: *css.PropertyHandlerContext) void { + if (this.direction) |direction| { + this.direction = null; + this.decls.append(context.allocator, css.Property{ .direction = direction }) catch bun.outOfMemory(); + } + // if (this.unicode_bidi) |unicode_bidi| { + // this.unicode_bidi = null; + // this.decls.append(context.allocator, css.Property{ .unicode_bidi = unicode_bidi }) catch bun.outOfMemory(); + // } + + // TODO: + // this.background.finalize(&this.decls, context); + } + + pub fn handleProperty(this: *DeclarationHandler, property: *const css.Property, context: *css.PropertyHandlerContext) bool { + _ = this; // autofix + _ = property; // autofix + _ = context; // autofix + // TODO + return false; + } + pub fn default() DeclarationHandler { - return .{}; + return .{ + .decls = .{}, + .direction = null, + }; } }; diff --git a/src/css/dependencies.zig b/src/css/dependencies.zig index c75bc2134e..7d922244dd 100644 --- a/src/css/dependencies.zig +++ b/src/css/dependencies.zig @@ -41,6 +41,14 @@ pub const Location = struct { .column = loc.column, }; } + + pub fn hash(this: *const @This(), hasher: *std.hash.Wyhash) void { + return css.implementHash(@This(), this, hasher); + } + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } }; /// An `@import` dependency. diff --git a/src/css/error.zig b/src/css/error.zig index 76671cab27..3132aa21a2 100644 --- a/src/css/error.zig +++ b/src/css/error.zig @@ -140,6 +140,18 @@ pub const ErrorLocation = struct { line: u32, /// The column number, starting from 1. column: u32, + + pub fn withFilename(this: ErrorLocation, filename: []const u8) ErrorLocation { + return ErrorLocation{ + .filename = filename, + .line = this.line, + .column = this.column, + }; + } + + pub fn format(this: *const @This(), comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void { + try writer.print("{s}:{d}:{d}", .{ this.filename, this.line, this.column }); + } }; /// A printer error type. @@ -272,6 +284,7 @@ pub const SelectorError = union(enum) { unexpected_token_in_attribute_selector: css.Token, /// An unsupported pseudo class or pseudo element was encountered. unsupported_pseudo_class_or_element: []const u8, + unexpected_selector_after_pseudo_element: css.Token, pub fn format(this: @This(), comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void { return switch (this) { @@ -304,6 +317,9 @@ pub fn ErrorWithLocation(comptime T: type) type { }; } +pub const MinifyErr = error{ + minify_err, +}; pub const MinifyError = ErrorWithLocation(MinifyErrorKind); /// A transformation error. pub const MinifyErrorKind = union(enum) { @@ -322,4 +338,18 @@ pub const MinifyErrorKind = union(enum) { /// The source location of the `@custom-media` rule with unsupported boolean logic. custom_media_loc: Location, }, + + pub fn format(this: *const @This(), comptime _: []const u8, _: anytype, writer: anytype) !void { + return switch (this.*) { + .circular_custom_media => |name| try writer.print("Circular @custom-media rule: \"{s}\"", .{name.name}), + .custom_media_not_defined => |name| try writer.print("Custom media rule \"{s}\" not defined", .{name.name}), + .unsupported_custom_media_boolean_logic => |custom_media_loc| try writer.print( + "Unsupported boolean logic in custom media rule at line {d}, column {d}", + .{ + custom_media_loc.custom_media_loc.line, + custom_media_loc.custom_media_loc.column, + }, + ), + }; + } }; diff --git a/src/css/generics.zig b/src/css/generics.zig new file mode 100644 index 0000000000..11d749a4f6 --- /dev/null +++ b/src/css/generics.zig @@ -0,0 +1,411 @@ +const std = @import("std"); +const Allocator = std.mem.Allocator; +const bun = @import("root").bun; +const logger = bun.logger; +const Log = logger.Log; + +const ArrayList = std.ArrayListUnmanaged; + +const css = @import("./css_parser.zig"); +const css_values = css.css_values; + +const Parser = css.Parser; +const ParserOptions = css.ParserOptions; +const Result = css.Result; +const Printer = css.Printer; +const PrintErr = css.PrintErr; +const CSSNumber = css.CSSNumber; +const CSSNumberFns = css.CSSNumberFns; +const CSSInteger = css.CSSInteger; +const CSSIntegerFns = css.CSSIntegerFns; +const CustomIdent = css.CustomIdent; +const CustomIdentFns = css.CustomIdentFns; +const DashedIdent = css.DashedIdent; +const DashedIdentFns = css.DashedIdentFns; +const Ident = css.Ident; +const IdentFns = css.IdentFns; + +pub inline fn parseWithOptions(comptime T: type, input: *Parser, options: *const ParserOptions) Result(T) { + if (T != f32 and T != i32 and @hasDecl(T, "parseWithOptions")) return T.parseWithOptions(input, options); + if (comptime bun.meta.looksLikeListContainerType(T)) |result| { + switch (result.list) { + .array_list => return input.parseCommaSeparated(result.child, parseFor(result.child)), + .baby_list => {}, + .small_list => {}, + } + } + return switch (T) { + f32 => CSSNumberFns.parse(input), + CSSInteger => CSSIntegerFns.parse(input), + CustomIdent => CustomIdentFns.parse(input), + DashedIdent => DashedIdentFns.parse(input), + Ident => IdentFns.parse(input), + else => T.parse(input), + }; +} + +pub inline fn parse(comptime T: type, input: *Parser) Result(T) { + if (comptime @typeInfo(T) == .Pointer) { + const TT = std.meta.Child(T); + return switch (parse(TT, input)) { + .result => |v| .{ .result = bun.create(input.allocator(), TT, v) }, + .err => |e| .{ .err = e }, + }; + } + if (comptime @typeInfo(T) == .Optional) { + const TT = std.meta.Child(T); + return .{ .result = parse(TT, input).asValue() }; + } + if (comptime bun.meta.looksLikeListContainerType(T)) |result| { + switch (result.list) { + .array_list => return input.parseCommaSeparated(result.child, parseFor(result.child)), + .baby_list => {}, + .small_list => {}, + } + } + return switch (T) { + f32 => CSSNumberFns.parse(input), + CSSInteger => CSSIntegerFns.parse(input), + CustomIdent => CustomIdentFns.parse(input), + DashedIdent => DashedIdentFns.parse(input), + Ident => IdentFns.parse(input), + else => T.parse(input), + }; +} + +pub inline fn parseFor(comptime T: type) @TypeOf(struct { + fn parsefn(input: *Parser) Result(T) { + return parse(T, input); + } +}.parsefn) { + return struct { + fn parsefn(input: *Parser) Result(T) { + return parse(T, input); + } + }.parsefn; +} + +pub fn hasToCss(comptime T: type) bool { + const tyinfo = @typeInfo(T); + if (comptime T == []const u8) return false; + if (tyinfo == .Pointer) { + const TT = std.meta.Child(T); + return hasToCss(TT); + } + if (tyinfo == .Optional) { + const TT = std.meta.Child(T); + return hasToCss(TT); + } + if (comptime bun.meta.looksLikeListContainerType(T)) |result| { + switch (result.list) { + .array_list => return true, + .baby_list => return true, + .small_list => return true, + } + } + return switch (T) { + f32 => true, + else => @hasDecl(T, "toCss"), + }; +} + +pub inline fn toCss(comptime T: type, this: *const T, comptime W: type, dest: *Printer(W)) PrintErr!void { + if (@typeInfo(T) == .Pointer) { + const TT = std.meta.Child(T); + return toCss(TT, this.*, W, dest); + } + if (@typeInfo(T) == .Optional) { + const TT = std.meta.Child(T); + + if (this.*) |*val| { + return toCss(TT, val, W, dest); + } + return; + } + if (comptime bun.meta.looksLikeListContainerType(T)) |result| { + switch (result.list) { + .array_list => { + return css.to_css.fromList(result.child, this, W, dest); + }, + .baby_list => {}, + .small_list => {}, + } + } + return switch (T) { + f32 => CSSNumberFns.toCss(this, W, dest), + CSSInteger => CSSIntegerFns.toCss(this, W, dest), + CustomIdent => CustomIdentFns.toCss(this, W, dest), + DashedIdent => DashedIdentFns.toCss(this, W, dest), + Ident => IdentFns.toCss(this, W, dest), + else => T.toCss(this, W, dest), + }; +} + +pub fn eqlList(comptime T: type, lhs: *const ArrayList(T), rhs: *const ArrayList(T)) bool { + if (lhs.items.len != rhs.items.len) return false; + for (lhs.items, 0..) |*item, i| { + if (!eql(T, item, &rhs.items[i])) return false; + } + return true; +} + +pub inline fn eql(comptime T: type, lhs: *const T, rhs: *const T) bool { + const tyinfo = comptime @typeInfo(T); + if (comptime tyinfo == .Pointer) { + if (comptime T == []const u8) return bun.strings.eql(lhs.*, rhs.*); + if (comptime tyinfo.Pointer.size == .One) { + const TT = std.meta.Child(T); + return eql(TT, lhs.*, rhs.*); + } else if (comptime tyinfo.Pointer.size == .Slice) { + if (lhs.*.len != rhs.*.len) return false; + for (lhs.*[0..], rhs.*[0..]) |*a, *b| { + if (!eql(tyinfo.Pointer.child, a, b)) return false; + } + return true; + } else { + @compileError("Unsupported pointer size: " ++ @tagName(tyinfo.Pointer.size) ++ " (" ++ @typeName(T) ++ ")"); + } + } + if (comptime tyinfo == .Optional) { + const TT = std.meta.Child(T); + if (lhs.* != null and rhs.* != null) return eql(TT, &lhs.*.?, &rhs.*.?); + return false; + } + if (comptime bun.meta.isSimpleEqlType(T)) { + return lhs.* == rhs.*; + } + if (comptime bun.meta.looksLikeListContainerType(T)) |result| { + return switch (result.list) { + .array_list => eqlList(result.child, lhs, rhs), + .baby_list => return lhs.eql(rhs), + .small_list => lhs.eql(rhs), + }; + } + return switch (T) { + f32 => lhs.* == rhs.*, + CSSInteger => lhs.* == rhs.*, + CustomIdent, DashedIdent, Ident => bun.strings.eql(lhs.v, rhs.v), + []const u8 => bun.strings.eql(lhs.*, rhs.*), + css.VendorPrefix => css.VendorPrefix.eq(lhs.*, rhs.*), + else => T.eql(lhs, rhs), + }; +} + +pub inline fn deepClone(comptime T: type, this: *const T, allocator: Allocator) T { + const tyinfo = comptime @typeInfo(T); + if (comptime tyinfo == .Pointer) { + if (comptime tyinfo.Pointer.size == .One) { + const TT = std.meta.Child(T); + return bun.create(allocator, TT, deepClone(TT, this.*, allocator)); + } + if (comptime tyinfo.Pointer.size == .Slice) { + var slice = allocator.alloc(tyinfo.Pointer.child, this.len) catch bun.outOfMemory(); + if (comptime bun.meta.isSimpleCopyType(tyinfo.Pointer.child) or tyinfo.Pointer.child == []const u8) { + @memcpy(slice, this.*); + } else { + for (this.*, 0..) |*e, i| { + slice[i] = deepClone(tyinfo.Pointer.child, &e, allocator); + } + } + return slice; + } + @compileError("Deep clone not supported for this kind of pointer: " ++ @tagName(tyinfo.Pointer.size) ++ " (" ++ @typeName(T) ++ ")"); + } + if (comptime tyinfo == .Optional) { + const TT = std.meta.Child(T); + if (this.* != null) return deepClone(TT, &this.*.?, allocator); + return null; + } + if (comptime bun.meta.isSimpleCopyType(T)) { + return this.*; + } + if (comptime bun.meta.looksLikeListContainerType(T)) |result| { + return switch (result.list) { + .array_list => css.deepClone(result.child, allocator, this), + .baby_list => { + var ret = bun.BabyList(result.child){ + .ptr = (allocator.alloc(result.child, this.len) catch bun.outOfMemory()).ptr, + .len = this.len, + .cap = this.len, + }; + for (this.sliceConst(), ret.ptr[0..this.len]) |*old, *new| { + new.* = bun.css.generic.deepClone(result.child, old, allocator); + } + return ret; + }, + .small_list => this.deepClone(allocator), + }; + } + // Strings in the CSS parser are always arena allocated + // So it is safe to skip const strings as they will never be mutated + if (comptime T == []const u8) { + return this.*; + } + + if (!@hasDecl(T, "deepClone")) { + @compileError(@typeName(T) ++ " does not have a deepClone() function"); + } + + return T.deepClone(this, allocator); +} + +const Angle = css_values.angle.Angle; +pub inline fn tryFromAngle(comptime T: type, angle: Angle) ?T { + return switch (T) { + CSSNumber => CSSNumberFns.tryFromAngle(angle), + Angle => return Angle.tryFromAngle(angle), + else => T.tryFromAngle(angle), + }; +} + +pub inline fn trySign(comptime T: type, val: *const T) ?f32 { + return switch (T) { + CSSNumber => CSSNumberFns.sign(val), + else => { + if (@hasDecl(T, "sign")) return T.sign(val); + return T.trySign(val); + }, + }; +} + +pub inline fn tryMap( + comptime T: type, + val: *const T, + comptime map_fn: *const fn (a: f32) f32, +) ?T { + return switch (T) { + CSSNumber => map_fn(val.*), + else => { + if (@hasDecl(T, "map")) return T.map(val, map_fn); + return T.tryMap(val, map_fn); + }, + }; +} + +pub inline fn tryOpTo( + comptime T: type, + comptime R: type, + lhs: *const T, + rhs: *const T, + ctx: anytype, + comptime op_fn: *const fn (@TypeOf(ctx), a: f32, b: f32) R, +) ?R { + return switch (T) { + CSSNumber => op_fn(ctx, lhs.*, rhs.*), + else => { + if (@hasDecl(T, "opTo")) return T.opTo(lhs, rhs, R, ctx, op_fn); + return T.tryOpTo(lhs, rhs, R, ctx, op_fn); + }, + }; +} + +pub inline fn tryOp( + comptime T: type, + lhs: *const T, + rhs: *const T, + ctx: anytype, + comptime op_fn: *const fn (@TypeOf(ctx), a: f32, b: f32) f32, +) ?T { + return switch (T) { + Angle => Angle.tryOp(lhs, rhs, ctx, op_fn), + CSSNumber => op_fn(ctx, lhs.*, rhs.*), + else => { + if (@hasDecl(T, "op")) return T.op(lhs, rhs, ctx, op_fn); + return T.tryOp(lhs, rhs, ctx, op_fn); + }, + }; +} + +pub inline fn partialCmp(comptime T: type, lhs: *const T, rhs: *const T) ?std.math.Order { + return switch (T) { + f32 => partialCmpF32(lhs, rhs), + CSSInteger => std.math.order(lhs.*, rhs.*), + css_values.angle.Angle => css_values.angle.Angle.partialCmp(lhs, rhs), + else => T.partialCmp(lhs, rhs), + }; +} + +pub inline fn partialCmpF32(lhs: *const f32, rhs: *const f32) ?std.math.Order { + const lte = lhs.* <= rhs.*; + const rte = lhs.* >= rhs.*; + if (!lte and !rte) return null; + if (!lte and rte) return .gt; + if (lte and !rte) return .lt; + return .eq; +} + +pub const HASH_SEED: u64 = 0; + +pub fn hashArrayList(comptime V: type, this: *const ArrayList(V), hasher: *std.hash.Wyhash) void { + for (this.items) |*item| { + hash(V, item, hasher); + } +} +pub fn hashBabyList(comptime V: type, this: *const bun.BabyList(V), hasher: *std.hash.Wyhash) void { + for (this.sliceConst()) |*item| { + hash(V, item, hasher); + } +} + +pub fn hasHash(comptime T: type) bool { + const tyinfo = @typeInfo(T); + if (comptime T == []const u8) return true; + if (comptime bun.meta.isSimpleEqlType(T)) return true; + if (tyinfo == .Pointer) { + const TT = std.meta.Child(T); + return hasHash(TT); + } + if (tyinfo == .Optional) { + const TT = std.meta.Child(T); + return hasHash(TT); + } + if (comptime bun.meta.looksLikeListContainerType(T)) |result| { + switch (result.list) { + .array_list => return true, + .baby_list => return true, + .small_list => return true, + } + } + return switch (T) { + else => @hasDecl(T, "hash"), + }; +} + +pub fn hash(comptime T: type, this: *const T, hasher: *std.hash.Wyhash) void { + if (comptime T == void) return; + const tyinfo = @typeInfo(T); + if (comptime tyinfo == .Pointer and T != []const u8) { + const TT = std.meta.Child(T); + if (tyinfo.Pointer.size == .One) { + return hash(TT, this.*, hasher); + } else if (tyinfo.Pointer.size == .Slice) { + for (this.*) |*item| { + hash(TT, item, hasher); + } + return; + } else { + @compileError("Can't hash this pointer type: " ++ @typeName(T)); + } + } + if (comptime @typeInfo(T) == .Optional) { + const TT = std.meta.Child(T); + if (this.* != null) return hash(TT, &this.*.?, hasher); + return; + } + if (comptime bun.meta.looksLikeListContainerType(T)) |result| { + switch (result.list) { + .array_list => return hashArrayList(result.child, this, hasher), + .baby_list => return hashBabyList(result.child, this, hasher), + .small_list => return this.hash(hasher), + } + } + if (comptime bun.meta.isSimpleEqlType(T)) { + const bytes = std.mem.asBytes(&this); + hasher.update(bytes); + return; + } + return switch (T) { + []const u8 => hasher.update(this.*), + else => T.hash(this, hasher), + }; +} diff --git a/src/css/media_query.zig b/src/css/media_query.zig index 4cf4b1a630..23c7ef045a 100644 --- a/src/css/media_query.zig +++ b/src/css/media_query.zig @@ -567,7 +567,7 @@ fn parseParenBlock( /// A [media feature](https://drafts.csswg.org/mediaqueries/#typedef-media-feature) pub const MediaFeature = QueryFeature(MediaFeatureId); -const MediaFeatureId = enum { +pub const MediaFeatureId = enum { /// The [width](https://w3c.github.io/csswg-drafts/mediaqueries-5/#width) media feature. width, /// The [height](https://w3c.github.io/csswg-drafts/mediaqueries-5/#height) media feature. diff --git a/src/css/printer.zig b/src/css/printer.zig index 9d581911ac..9d7b029e2a 100644 --- a/src/css/printer.zig +++ b/src/css/printer.zig @@ -207,7 +207,7 @@ pub fn Printer(comptime Writer: type) type { pub fn printImportRecord(this: *This, import_record_idx: u32) PrintErr!void { if (this.import_records) |import_records| { const import_record = import_records.at(import_record_idx); - const a, const b = bun.bundle_v2.cheapPrefixNormalizer(this.public_path, import_record.path.pretty); + const a, const b = bun.bundle_v2.cheapPrefixNormalizer(this.public_path, import_record.path.text); try this.writeStr(a); try this.writeStr(b); return; @@ -221,6 +221,10 @@ pub fn Printer(comptime Writer: type) type { unreachable; } + pub inline fn getImportRecordUrl(this: *This, import_record_idx: u32) PrintErr![]const u8 { + return (try this.importRecord(import_record_idx)).path.text; + } + pub fn context(this: *const Printer(Writer)) ?*const css.StyleContext { return this.ctx; } @@ -233,6 +237,18 @@ pub fn Printer(comptime Writer: type) type { return this.writeStr(str) catch std.mem.Allocator.Error.OutOfMemory; } + pub fn writeComment(this: *This, comment: []const u8) PrintErr!void { + _ = this.dest.writeAll(comment) catch { + return this.addFmtError(); + }; + const new_lines = std.mem.count(u8, comment, "\n"); + this.line += @intCast(new_lines); + this.col = 0; + const last_line_start = comment.len - (std.mem.lastIndexOfScalar(u8, comment, '\n') orelse comment.len); + this.col += @intCast(last_line_start); + return; + } + /// Writes a raw string to the underlying destination. /// /// NOTE: Is is assumed that the string does not contain any newline characters. diff --git a/src/css/properties/align.zig b/src/css/properties/align.zig index 964ad7907f..2f631c0584 100644 --- a/src/css/properties/align.zig +++ b/src/css/properties/align.zig @@ -24,7 +24,44 @@ pub const AlignContent = union(enum) { overflow: ?OverflowPosition, /// A content position keyword. value: ContentPosition, + + pub fn toInner(this: *const @This()) ContentPositionInner { + return .{ + .overflow = this.overflow, + .value = this.value, + }; + } + + pub fn __generateToCss() void {} + + pub fn parse(input: *css.Parser) css.Result(@This()) { + const overflow = OverflowPosition.parse(input).asValue(); + const value = switch (ContentPosition.parse(input)) { + .result => |v| v, + .err => |e| return .{ .err = e }, + }; + return .{ .result = .{ .overflow = overflow, .value = value } }; + } + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } }, + + pub usingnamespace css.DeriveParse(@This()); + pub usingnamespace css.DeriveToCss(@This()); + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } }; /// A [``](https://www.w3.org/TR/css-align-3/#typedef-baseline-position) value, @@ -34,6 +71,51 @@ pub const BaselinePosition = enum { first, /// The last baseline. last, + + pub fn parse(input: *css.Parser) css.Result(@This()) { + const location = input.currentSourceLocation(); + const ident = switch (input.expectIdent()) { + .err => |e| return .{ .err = e }, + .result => |v| v, + }; + + const BaselinePositionIdent = enum { + baseline, + first, + last, + }; + + const BaselinePositionMap = bun.ComptimeEnumMap(BaselinePositionIdent); + if (BaselinePositionMap.get(ident)) |value| + switch (value) { + .baseline => return .{ .result = BaselinePosition.first }, + .first => { + if (input.expectIdentMatching("baseline").asErr()) |e| return .{ .err = e }; + return .{ .result = BaselinePosition.first }; + }, + .last => { + if (input.expectIdentMatching("baseline").asErr()) |e| return .{ .err = e }; + return .{ .result = BaselinePosition.last }; + }, + } + else + return .{ .err = location.newUnexpectedTokenError(.{ .ident = ident }) }; + } + + pub fn toCss(this: *const BaselinePosition, comptime W: type, dest: *css.Printer(W)) css.PrintErr!void { + return switch (this.*) { + .first => try dest.writeStr("baseline"), + .last => try dest.writeStr("last baseline"), + }; + } + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } }; /// A value for the [justify-content](https://www.w3.org/TR/css-align-3/#propdef-justify-content) property. @@ -48,17 +130,124 @@ pub const JustifyContent = union(enum) { value: ContentPosition, /// An overflow alignment mode. overflow: ?OverflowPosition, + + pub fn toInner(this: *const @This()) ContentPositionInner { + return .{ + .overflow = this.overflow, + .value = this.value, + }; + } + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } }, /// Justify to the left. left: struct { /// An overflow alignment mode. overflow: ?OverflowPosition, + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } }, /// Justify to the right. right: struct { /// An overflow alignment mode. overflow: ?OverflowPosition, + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } }, + + pub fn parse(input: *css.Parser) css.Result(@This()) { + if (input.expectIdentMatching("normal").isOk()) { + return .{ .result = .normal }; + } + + if (ContentDistribution.parse(input).asValue()) |val| { + return .{ .result = .{ .content_distribution = val } }; + } + + const overflow = OverflowPosition.parse(input).asValue(); + if (ContentPosition.parse(input).asValue()) |content_position| { + return .{ .result = .{ + .content_position = .{ + .overflow = overflow, + .value = content_position, + }, + } }; + } + + const location = input.currentSourceLocation(); + const ident = switch (input.expectIdent()) { + .result => |v| v, + .err => |e| return .{ .err = e }, + }; + + const JustifyContentIdent = enum { + left, + right, + }; + + const JustifyContentIdentMap = bun.ComptimeEnumMap(JustifyContentIdent); + if (JustifyContentIdentMap.get(ident)) |value| + return switch (value) { + .left => .{ .result = .{ .left = .{ .overflow = overflow } } }, + .right => .{ .result = .{ .right = .{ .overflow = overflow } } }, + } + else + return .{ .err = location.newUnexpectedTokenError(.{ .ident = ident }) }; + } + + pub fn toCss(this: *const @This(), comptime W: type, dest: *css.Printer(W)) css.PrintErr!void { + return switch (this.*) { + .normal => dest.writeStr("normal"), + .content_distribution => |value| value.toCss(W, dest), + .content_position => |*cp| { + if (cp.overflow) |*overflow| { + try overflow.toCss(W, dest); + try dest.writeStr(" "); + } + return cp.value.toCss(W, dest); + }, + .left => |*l| { + if (l.overflow) |*overflow| { + try overflow.toCss(W, dest); + try dest.writeStr(" "); + } + return dest.writeStr("left"); + }, + .right => |*r| { + if (r.overflow) |*overflow| { + try overflow.toCss(W, dest); + try dest.writeStr(" "); + } + return dest.writeStr("right"); + }, + }; + } + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } }; /// A value for the [align-self](https://www.w3.org/TR/css-align-3/#align-self-property) property. @@ -77,7 +266,45 @@ pub const AlignSelf = union(enum) { overflow: ?OverflowPosition, /// A self position keyword. value: SelfPosition, + + pub fn toInner(this: *const @This()) SelfPositionInner { + return .{ + .overflow = this.overflow, + .value = this.value, + }; + } + + pub fn __generateToCss() void {} + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } + + pub fn parse(input: *css.Parser) css.Result(@This()) { + const overflow = OverflowPosition.parse(input).asValue(); + const self_position = switch (SelfPosition.parse(input)) { + .result => |v| v, + .err => |e| return .{ .err = e }, + }; + return .{ + .result = .{ + .overflow = overflow, + .value = self_position, + }, + }; + } }, + + pub usingnamespace css.DeriveParse(@This()); + pub usingnamespace css.DeriveToCss(@This()); + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } }; /// A value for the [justify-self](https://www.w3.org/TR/css-align-3/#justify-self-property) property. @@ -96,17 +323,123 @@ pub const JustifySelf = union(enum) { value: SelfPosition, /// An overflow alignment mode. overflow: ?OverflowPosition, + + pub fn toInner(this: *const @This()) SelfPositionInner { + return .{ + .overflow = this.overflow, + .value = this.value, + }; + } + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } }, /// Item is justified to the left. left: struct { /// An overflow alignment mode. overflow: ?OverflowPosition, + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } }, /// Item is justified to the right. right: struct { /// An overflow alignment mode. overflow: ?OverflowPosition, + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } }, + + pub fn parse(input: *css.Parser) css.Result(@This()) { + if (input.tryParse(css.Parser.expectIdentMatching, .{"auto"}).isOk()) { + return .{ .result = .auto }; + } + + if (input.tryParse(css.Parser.expectIdentMatching, .{"normal"}).isOk()) { + return .{ .result = .normal }; + } + + if (input.tryParse(css.Parser.expectIdentMatching, .{"stretch"}).isOk()) { + return .{ .result = .stretch }; + } + + if (input.tryParse(BaselinePosition.parse, .{}).asValue()) |val| { + return .{ .result = .{ .baseline_position = val } }; + } + + const overflow = input.tryParse(OverflowPosition.parse, .{}).asValue(); + if (input.tryParse(SelfPosition.parse, .{}).asValue()) |self_position| { + return .{ .result = .{ .self_position = .{ .overflow = overflow, .value = self_position } } }; + } + + const location = input.currentSourceLocation(); + const ident = switch (input.expectIdent()) { + .result => |v| v, + .err => |e| return .{ .err = e }, + }; + const Enum = enum { left, right }; + const Map = bun.ComptimeEnumMap(Enum); + if (Map.get(ident)) |val| return .{ .result = switch (val) { + .left => .{ .left = .{ .overflow = overflow } }, + .right => .{ .right = .{ .overflow = overflow } }, + } }; + return .{ .err = location.newUnexpectedTokenError(.{ .ident = ident }) }; + } + + pub fn toCss(this: *const JustifySelf, comptime W: type, dest: *css.Printer(W)) css.PrintErr!void { + return switch (this.*) { + .auto => try dest.writeStr("auto"), + .normal => try dest.writeStr("normal"), + .stretch => try dest.writeStr("stretch"), + .baseline_position => |*baseline_position| baseline_position.toCss(W, dest), + .self_position => |*self_position| { + if (self_position.overflow) |*overflow| { + try overflow.toCss(W, dest); + try dest.writeStr(" "); + } + + try self_position.value.toCss(W, dest); + }, + .left => |*left| { + if (left.overflow) |*overflow| { + try overflow.toCss(W, dest); + try dest.writeStr(" "); + } + try dest.writeStr("left"); + }, + .right => |*right| { + if (right.overflow) |*overflow| { + try overflow.toCss(W, dest); + try dest.writeStr(" "); + } + try dest.writeStr("right"); + }, + }; + } + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } }; /// A value for the [align-items](https://www.w3.org/TR/css-align-3/#align-items-property) property. @@ -123,7 +456,49 @@ pub const AlignItems = union(enum) { overflow: ?OverflowPosition, /// A self position keyword. value: SelfPosition, + + pub fn toInner(this: *const @This()) SelfPositionInner { + return .{ + .overflow = this.overflow, + .value = this.value, + }; + } + + pub fn parse(input: *css.Parser) css.Result(@This()) { + const overflow = OverflowPosition.parse(input).asValue(); + const self_position = switch (SelfPosition.parse(input)) { + .result => |v| v, + .err => |e| return .{ .err = e }, + }; + return .{ + .result = .{ + .overflow = overflow, + .value = self_position, + }, + }; + } + + pub fn __generateToCss() void {} + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } }, + + pub usingnamespace css.DeriveParse(@This()); + pub usingnamespace css.DeriveToCss(@This()); + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } }; /// A value for the [justify-items](https://www.w3.org/TR/css-align-3/#justify-items-property) property. @@ -140,19 +515,125 @@ pub const JustifyItems = union(enum) { value: SelfPosition, /// An overflow alignment mode. overflow: ?OverflowPosition, + + pub fn toInner(this: *const @This()) SelfPositionInner { + return .{ + .overflow = this.overflow, + .value = this.value, + }; + } + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } }, /// Items are justified to the left, with an optional overflow position. left: struct { /// An overflow alignment mode. overflow: ?OverflowPosition, + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } }, /// Items are justified to the right, with an optional overflow position. right: struct { /// An overflow alignment mode. overflow: ?OverflowPosition, + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } }, /// A legacy justification keyword. legacy: LegacyJustify, + + pub fn parse(input: *css.Parser) css.Result(@This()) { + if (input.tryParse(css.Parser.expectIdentMatching, .{"normal"}).isOk()) { + return .{ .result = .normal }; + } + + if (input.tryParse(css.Parser.expectIdentMatching, .{"stretch"}).isOk()) { + return .{ .result = .stretch }; + } + + if (input.tryParse(BaselinePosition.parse, .{}).asValue()) |val| { + return .{ .result = .{ .baseline_position = val } }; + } + + if (input.tryParse(LegacyJustify.parse, .{}).asValue()) |val| { + return .{ .result = .{ .legacy = val } }; + } + + const overflow = input.tryParse(OverflowPosition.parse, .{}).asValue(); + if (input.tryParse(SelfPosition.parse, .{}).asValue()) |self_position| { + return .{ .result = .{ .self_position = .{ .overflow = overflow, .value = self_position } } }; + } + + const location = input.currentSourceLocation(); + const ident = switch (input.expectIdent()) { + .result => |v| v, + .err => |e| return .{ .err = e }, + }; + + const Enum = enum { left, right }; + const Map = bun.ComptimeEnumMap(Enum); + if (Map.get(ident)) |val| return .{ .result = switch (val) { + .left => .{ .left = .{ .overflow = overflow } }, + .right => .{ .right = .{ .overflow = overflow } }, + } }; + return .{ .err = location.newUnexpectedTokenError(.{ .ident = ident }) }; + } + + pub fn toCss(this: *const JustifyItems, comptime W: type, dest: *css.Printer(W)) css.PrintErr!void { + switch (this.*) { + .normal => try dest.writeStr("normal"), + .stretch => try dest.writeStr("stretch"), + .baseline_position => |*val| try val.toCss(W, dest), + .self_position => |*sp| { + if (sp.overflow) |*overflow| { + try overflow.toCss(W, dest); + try dest.writeStr(" "); + } + try sp.value.toCss(W, dest); + }, + .left => |*l| { + if (l.overflow) |*overflow| { + try overflow.toCss(W, dest); + try dest.writeStr(" "); + } + try dest.writeStr("left"); + }, + .right => |*r| { + if (r.overflow) |*overflow| { + try overflow.toCss(W, dest); + try dest.writeStr(" "); + } + try dest.writeStr("right"); + }, + .legacy => |l| try l.toCss(W, dest), + } + } + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } }; /// A legacy justification keyword, as used in the `justify-items` property. @@ -163,6 +644,75 @@ pub const LegacyJustify = enum { right, /// Centered. center, + + pub fn parse(input: *css.Parser) css.Result(@This()) { + const location = input.currentSourceLocation(); + const ident = switch (input.expectIdent()) { + .err => |e| return .{ .err = e }, + .result => |v| v, + }; + + const LegacyJustifyIdent = enum { + legacy, + left, + right, + center, + }; + + const LegacyJustifyMap = bun.ComptimeEnumMap(LegacyJustifyIdent); + if (LegacyJustifyMap.get(ident)) |value| { + switch (value) { + .legacy => { + const inner_location = input.currentSourceLocation(); + const inner_ident = switch (input.expectIdent()) { + .err => |e| return .{ .err = e }, + .result => |v| v, + }; + const InnerEnum = enum { left, right, center }; + const InnerLegacyJustifyMap = bun.ComptimeEnumMap(InnerEnum); + if (InnerLegacyJustifyMap.get(inner_ident)) |inner_value| { + return switch (inner_value) { + .left => .{ .result = .left }, + .right => .{ .result = .right }, + .center => .{ .result = .center }, + }; + } else { + return .{ .err = inner_location.newUnexpectedTokenError(.{ .ident = inner_ident }) }; + } + }, + .left => { + if (input.expectIdentMatching("legacy").asErr()) |e| return .{ .err = e }; + return .{ .result = .left }; + }, + .right => { + if (input.expectIdentMatching("legacy").asErr()) |e| return .{ .err = e }; + return .{ .result = .right }; + }, + .center => { + if (input.expectIdentMatching("legacy").asErr()) |e| return .{ .err = e }; + return .{ .result = .center }; + }, + } + } + return .{ .err = location.newUnexpectedTokenError(.{ .ident = ident }) }; + } + + pub fn toCss(this: *const @This(), comptime W: type, dest: *css.Printer(W)) css.PrintErr!void { + try dest.writeStr("legacy "); + switch (this.*) { + .left => try dest.writeStr("left"), + .right => try dest.writeStr("right"), + .center => try dest.writeStr("center"), + } + } + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } }; /// A [gap](https://www.w3.org/TR/css-align-3/#column-row-gap) value, as used in the @@ -172,6 +722,17 @@ pub const GapValue = union(enum) { normal, /// An explicit length. length_percentage: LengthPercentage, + + pub usingnamespace css.DeriveParse(@This()); + pub usingnamespace css.DeriveToCss(@This()); + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } }; /// A value for the [gap](https://www.w3.org/TR/css-align-3/#gap-shorthand) shorthand property. @@ -181,12 +742,40 @@ pub const Gap = struct { /// The column gap. column: GapValue, - pub usingnamespace css.DefineShorthand(@This()); + pub usingnamespace css.DefineShorthand(@This(), css.PropertyIdTag.gap); - const PropertyFieldMap = .{ + pub const PropertyFieldMap = .{ .row = "row-gap", .column = "column-gap", }; + + pub fn parse(input: *css.Parser) css.Result(@This()) { + const row = switch (@call(.auto, @field(GapValue, "parse"), .{input})) { + .result => |v| v, + .err => |e| return .{ .err = e }, + }; + const column = switch (input.tryParse(@field(GapValue, "parse"), .{})) { + .result => |v| v, + .err => row, + }; + return .{ .result = .{ .row = row, .column = column } }; + } + + pub fn toCss(this: *const Gap, comptime W: type, dest: *css.Printer(W)) css.PrintErr!void { + try this.row.toCss(W, dest); + if (!this.column.eql(&this.row)) { + try dest.writeStr(" "); + try this.column.toCss(W, dest); + } + } + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } }; /// A value for the [place-items](https://www.w3.org/TR/css-align-3/#place-items-property) shorthand property. @@ -196,16 +785,69 @@ pub const PlaceItems = struct { /// The item justification. justify: JustifyItems, - pub usingnamespace css.DefineShorthand(@This()); + pub usingnamespace css.DefineShorthand(@This(), css.PropertyIdTag.@"place-items"); - const PropertyFieldMap = .{ + pub const PropertyFieldMap = .{ .@"align" = "align-items", .justify = "justify-items", }; - const VendorPrefixMap = .{ + pub const VendorPrefixMap = .{ .@"align" = true, }; + + pub fn parse(input: *css.Parser) css.Result(@This()) { + const @"align" = switch (@call(.auto, @field(AlignItems, "parse"), .{input})) { + .result => |v| v, + .err => |e| return .{ .err = e }, + }; + const justify = switch (input.tryParse(@field(JustifyItems, "parse"), .{})) { + .result => |v| v, + .err => switch (@"align") { + .normal => JustifyItems.normal, + .stretch => JustifyItems.stretch, + .baseline_position => |p| JustifyItems{ .baseline_position = p }, + .self_position => |sp| JustifyItems{ + .self_position = .{ + .overflow = if (sp.overflow) |o| o else null, + .value = sp.value, + }, + }, + }, + }; + + return .{ .result = .{ .@"align" = @"align", .justify = justify } }; + } + + pub fn toCss(this: *const PlaceItems, comptime W: type, dest: *css.Printer(W)) css.PrintErr!void { + try this.@"align".toCss(W, dest); + const is_equal = switch (this.justify) { + .normal => this.@"align".eql(&AlignItems{ .normal = {} }), + .stretch => this.@"align".eql(&AlignItems{ .stretch = {} }), + .baseline_position => |*p| brk: { + if (this.@"align" == .baseline_position) break :brk p.eql(&this.@"align".baseline_position); + break :brk false; + }, + .self_position => |*p| brk: { + if (this.@"align" == .self_position) break :brk p.toInner().eql(&this.@"align".self_position.toInner()); + break :brk false; + }, + else => false, + }; + + if (!is_equal) { + try dest.writeStr(" "); + try this.justify.toCss(W, dest); + } + } + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } }; /// A value for the [place-self](https://www.w3.org/TR/css-align-3/#place-self-property) shorthand property. @@ -215,16 +857,71 @@ pub const PlaceSelf = struct { /// The item justification. justify: JustifySelf, - pub usingnamespace css.DefineShorthand(@This()); + pub usingnamespace css.DefineShorthand(@This(), css.PropertyIdTag.@"place-self"); - const PropertyFieldMap = .{ + pub const PropertyFieldMap = .{ .@"align" = "align-self", .justify = "justify-self", }; - const VendorPrefixMap = .{ + pub const VendorPrefixMap = .{ .@"align" = true, }; + + pub fn parse(input: *css.Parser) css.Result(@This()) { + const @"align" = switch (@call(.auto, @field(AlignSelf, "parse"), .{input})) { + .result => |v| v, + .err => |e| return .{ .err = e }, + }; + const justify = switch (input.tryParse(@field(JustifySelf, "parse"), .{})) { + .result => |v| v, + .err => switch (@"align") { + .auto => JustifySelf.auto, + .normal => JustifySelf.normal, + .stretch => JustifySelf.stretch, + .baseline_position => |p| JustifySelf{ .baseline_position = p }, + .self_position => |sp| JustifySelf{ + .self_position = .{ + .overflow = if (sp.overflow) |o| o else null, + .value = sp.value, + }, + }, + }, + }; + + return .{ .result = .{ .@"align" = @"align", .justify = justify } }; + } + + pub fn toCss(this: *const PlaceSelf, comptime W: type, dest: *css.Printer(W)) css.PrintErr!void { + try this.@"align".toCss(W, dest); + const is_equal = switch (this.justify) { + .auto => true, + .normal => this.@"align" == .normal, + .stretch => this.@"align" == .stretch, + .baseline_position => |p| switch (this.@"align") { + .baseline_position => |p2| p.eql(&p2), + else => false, + }, + .self_position => |sp| brk: { + if (this.@"align" == .self_position) break :brk sp.toInner().eql(&this.@"align".self_position.toInner()); + break :brk false; + }, + else => false, + }; + + if (!is_equal) { + try dest.writeStr(" "); + try this.justify.toCss(W, dest); + } + } + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } }; /// A [``](https://www.w3.org/TR/css-align-3/#typedef-self-position) value. @@ -256,15 +953,71 @@ pub const PlaceContent = struct { pub usingnamespace css.DefineShorthand(@This(), css.PropertyIdTag.@"place-content"); - const PropertyFieldMap = .{ + pub const PropertyFieldMap = .{ .@"align" = css.PropertyIdTag.@"align-content", .justify = css.PropertyIdTag.@"justify-content", }; - const VendorPrefixMap = .{ + pub const VendorPrefixMap = .{ .@"align" = true, .justify = true, }; + + pub fn parse(input: *css.Parser) css.Result(@This()) { + const @"align" = switch (@call(.auto, @field(AlignContent, "parse"), .{input})) { + .result => |v| v, + .err => |e| return .{ .err = e }, + }; + const justify = switch (@call(.auto, @field(JustifyContent, "parse"), .{input})) { + .result => |v| v, + .err => |_| switch (@"align") { + .baseline_position => JustifyContent{ .content_position = .{ + .overflow = null, + .value = .start, + } }, + .normal => JustifyContent.normal, + .content_distribution => |value| JustifyContent{ .content_distribution = value }, + .content_position => |pos| JustifyContent{ .content_position = .{ + .overflow = if (pos.overflow) |*overflow| overflow.deepClone(input.allocator()) else null, + .value = pos.value.deepClone(input.allocator()), + } }, + }, + }; + + return .{ .result = .{ .@"align" = @"align", .justify = justify } }; + } + + pub fn toCss(this: *const PlaceContent, comptime W: type, dest: *css.Printer(W)) css.PrintErr!void { + try this.@"align".toCss(W, dest); + const is_equal = switch (this.justify) { + .normal => brk: { + if (this.@"align" == .normal) break :brk true; + break :brk false; + }, + .content_distribution => |*d| brk: { + if (this.@"align" == .content_distribution) break :brk d.eql(&this.@"align".content_distribution); + break :brk false; + }, + .content_position => |*p| brk: { + if (this.@"align" == .content_position) break :brk p.toInner().eql(&this.@"align".content_position.toInner()); + break :brk false; + }, + else => false, + }; + + if (!is_equal) { + try dest.writeStr(" "); + try this.justify.toCss(W, dest); + } + } + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } }; /// A [``](https://www.w3.org/TR/css-align-3/#typedef-content-distribution) value. @@ -308,3 +1061,25 @@ pub const ContentPosition = enum { pub usingnamespace css.DefineEnumProperty(@This()); }; + +pub const SelfPositionInner = struct { + /// An overflow alignment mode. + overflow: ?OverflowPosition, + /// A self position keyword. + value: SelfPosition, + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } +}; + +pub const ContentPositionInner = struct { + /// An overflow alignment mode. + overflow: ?OverflowPosition, + /// A content position keyword. + value: ContentPosition, + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } +}; diff --git a/src/css/properties/animation.zig b/src/css/properties/animation.zig index 92a52ac642..b6136db261 100644 --- a/src/css/properties/animation.zig +++ b/src/css/properties/animation.zig @@ -38,6 +38,14 @@ pub const AnimationName = union(enum) { // ~toCssImpl const This = @This(); + pub fn hash(this: *const @This(), hasher: *std.hash.Wyhash) void { + return css.implementHash(@This(), this, hasher); + } + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } + pub fn toCss(this: *const This, comptime W: type, dest: *Printer(W)) PrintErr!void { _ = this; // autofix _ = dest; // autofix diff --git a/src/css/properties/background.zig b/src/css/properties/background.zig index a77e66e925..7153ee7add 100644 --- a/src/css/properties/background.zig +++ b/src/css/properties/background.zig @@ -20,7 +20,9 @@ const Image = css.css_values.image.Image; const CssColor = css.css_values.color.CssColor; const Ratio = css.css_values.ratio.Ratio; const HorizontalPosition = css.css_values.position.HorizontalPosition; -const VerticalPosition = css.css_values.position.HorizontalPosition; +const VerticalPosition = css.css_values.position.VerticalPosition; + +const Position = css.css_values.position.Position; /// A value for the [background](https://www.w3.org/TR/css-backgrounds-3/#background) shorthand property. pub const Background = struct { @@ -40,6 +42,174 @@ pub const Background = struct { origin: BackgroundOrigin, /// How the background should be clipped. clip: BackgroundClip, + + pub fn parse(input: *css.Parser) css.Result(@This()) { + var color: ?CssColor = null; + var position: ?BackgroundPosition = null; + var size: ?BackgroundSize = null; + var image: ?Image = null; + var repeat: ?BackgroundRepeat = null; + var attachment: ?BackgroundAttachment = null; + var origin: ?BackgroundOrigin = null; + var clip: ?BackgroundClip = null; + + while (true) { + // TODO: only allowed on the last background. + if (color == null) { + if (input.tryParse(CssColor.parse, .{}).asValue()) |value| { + color = value; + continue; + } + } + + if (position == null) { + if (input.tryParse(BackgroundPosition.parse, .{}).asValue()) |value| { + position = value; + + size = input.tryParse(struct { + fn parse(i: *css.Parser) css.Result(BackgroundSize) { + if (i.expectDelim('/').asErr()) |e| return .{ .err = e }; + return BackgroundSize.parse(i); + } + }.parse, .{}).asValue(); + + continue; + } + } + + if (image == null) { + if (input.tryParse(Image.parse, .{}).asValue()) |value| { + image = value; + continue; + } + } + + if (repeat == null) { + if (input.tryParse(BackgroundRepeat.parse, .{}).asValue()) |value| { + repeat = value; + continue; + } + } + + if (attachment == null) { + if (input.tryParse(BackgroundAttachment.parse, .{}).asValue()) |value| { + attachment = value; + continue; + } + } + + if (origin == null) { + if (input.tryParse(BackgroundOrigin.parse, .{}).asValue()) |value| { + origin = value; + continue; + } + } + + if (clip == null) { + if (input.tryParse(BackgroundClip.parse, .{}).asValue()) |value| { + clip = value; + continue; + } + } + + break; + } + + if (clip == null) { + if (origin) |o| { + clip = @as(BackgroundClip, @enumFromInt(@intFromEnum(o))); + } + } + + return .{ .result = .{ + .image = image orelse Image.default(), + .color = color orelse CssColor.default(), + .position = position orelse BackgroundPosition.default(), + .repeat = repeat orelse BackgroundRepeat.default(), + .size = size orelse BackgroundSize.default(), + .attachment = attachment orelse BackgroundAttachment.default(), + .origin = origin orelse .@"padding-box", + .clip = clip orelse .@"border-box", + } }; + } + + pub fn toCss(this: *const Background, comptime W: type, dest: *Printer(W)) PrintErr!void { + var has_output = false; + + if (!this.color.eql(&CssColor.default())) { + try this.color.toCss(W, dest); + has_output = true; + } + + if (!this.image.eql(&Image.default())) { + if (has_output) try dest.writeStr(" "); + try this.image.toCss(W, dest); + has_output = true; + } + + const position: Position = this.position.intoPosition(); + if (!position.isZero() or !this.size.eql(&BackgroundSize.default())) { + if (has_output) { + try dest.writeStr(" "); + } + try position.toCss(W, dest); + + if (!this.size.eql(&BackgroundSize.default())) { + try dest.delim('/', true); + try this.size.toCss(W, dest); + } + + has_output = true; + } + + if (!this.repeat.eql(&BackgroundRepeat.default())) { + if (has_output) try dest.writeStr(" "); + try this.repeat.toCss(W, dest); + has_output = true; + } + + if (!this.attachment.eql(&BackgroundAttachment.default())) { + if (has_output) try dest.writeStr(" "); + try this.attachment.toCss(W, dest); + has_output = true; + } + + const output_padding_box = !this.origin.eql(&BackgroundOrigin.@"padding-box") or + (!this.clip.eqlOrigin(&BackgroundOrigin.@"border-box") and this.clip.isBackgroundBox()); + + if (output_padding_box) { + if (has_output) try dest.writeStr(" "); + try this.origin.toCss(W, dest); + has_output = true; + } + + if ((output_padding_box and !this.clip.eqlOrigin(&BackgroundOrigin.@"border-box")) or + !this.clip.eqlOrigin(&BackgroundOrigin.@"border-box")) + { + if (has_output) try dest.writeStr(" "); + + try this.clip.toCss(W, dest); + has_output = true; + } + + // If nothing was output, then this is the initial value, e.g. background: transparent + if (!has_output) { + if (dest.minify) { + // `0 0` is the shortest valid background value + try this.position.toCss(W, dest); + } else { + try dest.writeStr("none"); + } + } + } + + pub inline fn deepClone(this: *const @This(), allocator: Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } }; /// A value for the [background-size](https://www.w3.org/TR/css-backgrounds-3/#background-size) property. @@ -47,14 +217,73 @@ pub const BackgroundSize = union(enum) { /// An explicit background size. explicit: struct { /// The width of the background. - width: css.css_values.length.LengthPercentage, + width: css.css_values.length.LengthPercentageOrAuto, /// The height of the background. height: css.css_values.length.LengthPercentageOrAuto, + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } + + pub inline fn deepClone(this: *const @This(), allocator: Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } }, /// The `cover` keyword. Scales the background image to cover both the width and height of the element. cover, /// The `contain` keyword. Scales the background image so that it fits within the element. contain, + + pub fn parse(input: *css.Parser) css.Result(@This()) { + if (input.tryParse(LengthPercentageOrAuto.parse, .{}).asValue()) |width| { + const height = input.tryParse(LengthPercentageOrAuto.parse, .{}).unwrapOr(.auto); + return .{ .result = .{ .explicit = .{ .width = width, .height = height } } }; + } + + const location = input.currentSourceLocation(); + const ident = switch (input.expectIdent()) { + .result => |v| v, + .err => |e| return .{ .err = e }, + }; + + if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(ident, "cover")) { + return .{ .result = .cover }; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(ident, "contain")) { + return .{ .result = .contain }; + } else { + return .{ .err = location.newBasicUnexpectedTokenError(.{ .ident = ident }) }; + } + } + + pub fn toCss(this: *const BackgroundSize, comptime W: type, dest: *Printer(W)) PrintErr!void { + return switch (this.*) { + .cover => dest.writeStr("cover"), + .contain => dest.writeStr("contain"), + .explicit => |explicit| { + try explicit.width.toCss(W, dest); + if (explicit.height != .auto) { + try dest.writeStr(" "); + try explicit.height.toCss(W, dest); + } + return; + }, + }; + } + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } + + pub fn default() @This() { + return BackgroundSize{ .explicit = .{ + .width = .auto, + .height = .auto, + } }; + } + + pub inline fn deepClone(this: *const @This(), allocator: Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } }; /// A value for the [background-position](https://drafts.csswg.org/css-backgrounds/#background-position) shorthand property. @@ -70,6 +299,39 @@ pub const BackgroundPosition = struct { .x = css.PropertyIdTag.@"background-position-x", .y = css.PropertyIdTag.@"background-position-y", }; + + pub fn parse(input: *css.Parser) css.Result(@This()) { + const pos = switch (css.css_values.position.Position.parse(input)) { + .result => |v| v, + .err => |e| return .{ .err = e }, + }; + return .{ .result = BackgroundPosition.fromPosition(pos) }; + } + + pub fn toCss(this: *const BackgroundPosition, comptime W: type, dest: *Printer(W)) PrintErr!void { + const pos = this.intoPosition(); + return pos.toCss(W, dest); + } + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } + + pub fn default() @This() { + return BackgroundPosition.fromPosition(Position.default()); + } + + pub fn fromPosition(pos: Position) BackgroundPosition { + return BackgroundPosition{ .x = pos.x, .y = pos.y }; + } + + pub fn intoPosition(this: *const BackgroundPosition) Position { + return Position{ .x = this.x, .y = this.y }; + } + + pub inline fn deepClone(this: *const @This(), allocator: Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } }; /// A value for the [background-repeat](https://www.w3.org/TR/css-backgrounds-3/#background-repeat) property. @@ -78,6 +340,59 @@ pub const BackgroundRepeat = struct { x: BackgroundRepeatKeyword, /// A repeat style for the y direction. y: BackgroundRepeatKeyword, + + pub fn default() @This() { + return BackgroundRepeat{ + .x = .repeat, + .y = .repeat, + }; + } + + pub fn parse(input: *css.Parser) css.Result(@This()) { + const state = input.state(); + const ident = switch (input.expectIdent()) { + .result => |v| v, + .err => |e| return .{ .err = e }, + }; + + if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(ident, "repeat-x")) { + return .{ .result = .{ .x = .repeat, .y = .@"no-repeat" } }; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(ident, "repeat-y")) { + return .{ .result = .{ .x = .@"no-repeat", .y = .repeat } }; + } + + input.reset(&state); + + const x = switch (BackgroundRepeatKeyword.parse(input)) { + .result => |v| v, + .err => |e| return .{ .err = e }, + }; + + const y = input.tryParse(BackgroundRepeatKeyword.parse, .{}).unwrapOrNoOptmizations(x); + + return .{ .result = .{ .x = x, .y = y } }; + } + + pub fn toCss(this: *const BackgroundRepeat, comptime W: type, dest: *Printer(W)) PrintErr!void { + const Repeat = BackgroundRepeatKeyword.repeat; + const NoRepeat = BackgroundRepeatKeyword.@"no-repeat"; + + if (this.x == Repeat and this.y == NoRepeat) { + return dest.writeStr("repeat-x"); + } else if (this.x == NoRepeat and this.y == Repeat) { + return dest.writeStr("repeat-y"); + } else { + try this.x.toCss(W, dest); + if (this.y != this.x) { + try dest.writeStr(" "); + try this.y.toCss(W, dest); + } + } + } + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } }; /// A [``](https://www.w3.org/TR/css-backgrounds-3/#typedef-repeat-style) value, @@ -93,7 +408,7 @@ pub const BackgroundRepeatKeyword = enum { /// The image is scaled so that it repeats an even number of times. round, /// The image is placed once and not repeated in this direction. - noRepeat, + @"no-repeat", pub usingnamespace css.DefineEnumProperty(@This()); }; @@ -108,6 +423,10 @@ pub const BackgroundAttachment = enum { local, pub usingnamespace css.DefineEnumProperty(@This()); + + pub fn default() @This() { + return .scroll; + } }; /// A value for the [background-origin](https://www.w3.org/TR/css-backgrounds-3/#background-origin) property. @@ -136,6 +455,22 @@ pub const BackgroundClip = enum { text, pub usingnamespace css.DefineEnumProperty(@This()); + + pub fn eqlOrigin(this: *const @This(), other: *const BackgroundOrigin) bool { + return switch (this.*) { + .@"border-box" => other.* == .@"border-box", + .@"padding-box" => other.* == .@"padding-box", + .@"content-box" => other.* == .@"content-box", + else => false, + }; + } + + pub fn isBackgroundBox(this: *const @This()) bool { + return switch (this.*) { + .@"border-box", .@"padding-box", .@"content-box" => true, + else => false, + }; + } }; /// A value for the [aspect-ratio](https://drafts.csswg.org/css-sizing-4/#aspect-ratio) property. diff --git a/src/css/properties/border.zig b/src/css/properties/border.zig index 5f313b9c38..6f89d00d28 100644 --- a/src/css/properties/border.zig +++ b/src/css/properties/border.zig @@ -19,7 +19,7 @@ const DashedIdent = css.css_values.ident.DashedIdent; const Image = css.css_values.image.Image; const CssColor = css.css_values.color.CssColor; const Ratio = css.css_values.ratio.Ratio; -const Length = css.css_values.length.LengthValue; +const Length = css.css_values.length.Length; /// A value for the [border-top](https://www.w3.org/TR/css-backgrounds-3/#propdef-border-top) shorthand property. pub const BorderTop = GenericBorder(LineStyle, 0); @@ -54,6 +54,98 @@ pub fn GenericBorder(comptime S: type, comptime P: u8) type { style: S, /// The border color. color: CssColor, + + const This = @This(); + + pub fn parse(input: *css.Parser) css.Result(@This()) { + // Order doesn't matter + var color: ?CssColor = null; + var style: ?S = null; + var width: ?BorderSideWidth = null; + var any = false; + + while (true) { + if (width == null) { + if (input.tryParse(BorderSideWidth.parse, .{}).asValue()) |value| { + width = value; + any = true; + } + } + + if (style == null) { + if (input.tryParse(S.parse, .{}).asValue()) |value| { + style = value; + any = true; + continue; + } + } + + if (color == null) { + if (input.tryParse(CssColor.parse, .{}).asValue()) |value| { + color = value; + any = true; + continue; + } + } + break; + } + + if (any) { + return .{ + .result = This{ + .width = width orelse BorderSideWidth.medium, + .style = style orelse S.default(), + .color = color orelse CssColor.current_color, + }, + }; + } + + return .{ .err = input.newCustomError(css.ParserError.invalid_declaration) }; + } + + pub fn toCss(this: *const This, W: anytype, dest: *Printer(W)) PrintErr!void { + if (this.eql(&This.default())) { + try this.style.toCss(W, dest); + return; + } + + var needs_space = false; + if (!this.width.eql(&BorderSideWidth.default())) { + try this.width.toCss(W, dest); + needs_space = true; + } + if (!this.style.eql(&S.default())) { + if (needs_space) { + try dest.writeStr(" "); + } + try this.style.toCss(W, dest); + needs_space = true; + } + if (!this.color.eql(&CssColor{ .current_color = {} })) { + if (needs_space) { + try dest.writeStr(" "); + } + try this.color.toCss(W, dest); + needs_space = true; + } + return; + } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } + + pub fn eql(this: *const This, other: *const This) bool { + return this.width.eql(&other.width) and this.style.eql(&other.style) and this.color.eql(&other.color); + } + + pub inline fn default() This { + return This{ + .width = .medium, + .style = S.default(), + .color = CssColor.current_color, + }; + } }; } /// A [``](https://drafts.csswg.org/css-backgrounds/#typedef-line-style) value, used in the `border-style` property. @@ -81,6 +173,10 @@ pub const LineStyle = enum { double, pub usingnamespace css.DefineEnumProperty(@This()); + + pub fn default() LineStyle { + return .none; + } }; /// A value for the [border-width](https://www.w3.org/TR/css-backgrounds-3/#border-width) property. @@ -96,8 +192,38 @@ pub const BorderSideWidth = union(enum) { pub usingnamespace css.DeriveParse(@This()); pub usingnamespace css.DeriveToCss(@This()); + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } + + pub fn default() BorderSideWidth { + return .medium; + } + + pub fn eql(this: *const @This(), other: *const @This()) bool { + return switch (this.*) { + .thin => switch (other.*) { + .thin => true, + else => false, + }, + .medium => switch (other.*) { + .medium => true, + else => false, + }, + .thick => switch (other.*) { + .thick => true, + else => false, + }, + .length => switch (other.*) { + .length => this.length.eql(&other.length), + else => false, + }, + }; + } }; +// TODO: fallbacks /// A value for the [border-color](https://drafts.csswg.org/css-backgrounds/#propdef-border-color) shorthand property. pub const BorderColor = struct { top: CssColor, @@ -105,7 +231,8 @@ pub const BorderColor = struct { bottom: CssColor, left: CssColor, - pub usingnamespace css.DefineShorthand(@This(), css.PropertyIdTag.@"border-color"); + // TODO: bring this back + // pub usingnamespace css.DefineShorthand(@This(), css.PropertyIdTag.@"border-color"); pub usingnamespace css.DefineRectShorthand(@This(), CssColor); pub const PropertyFieldMap = .{ @@ -114,6 +241,14 @@ pub const BorderColor = struct { .bottom = css.PropertyIdTag.@"border-bottom-color", .left = css.PropertyIdTag.@"border-left-color", }; + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } }; /// A value for the [border-style](https://drafts.csswg.org/css-backgrounds/#propdef-border-style) shorthand property. @@ -123,7 +258,8 @@ pub const BorderStyle = struct { bottom: LineStyle, left: LineStyle, - pub usingnamespace css.DefineShorthand(@This(), css.PropertyIdTag.@"border-style"); + // TODO: bring this back + // pub usingnamespace css.DefineShorthand(@This(), css.PropertyIdTag.@"border-style"); pub usingnamespace css.DefineRectShorthand(@This(), LineStyle); pub const PropertyFieldMap = .{ @@ -132,6 +268,14 @@ pub const BorderStyle = struct { .bottom = css.PropertyIdTag.@"border-bottom-style", .left = css.PropertyIdTag.@"border-left-style", }; + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } }; /// A value for the [border-width](https://drafts.csswg.org/css-backgrounds/#propdef-border-width) shorthand property. @@ -141,7 +285,8 @@ pub const BorderWidth = struct { bottom: BorderSideWidth, left: BorderSideWidth, - pub usingnamespace css.DefineShorthand(@This(), css.PropertyIdTag.@"border-width"); + // TODO: bring this back + // pub usingnamespace css.DefineShorthand(@This(), css.PropertyIdTag.@"border-width"); pub usingnamespace css.DefineRectShorthand(@This(), BorderSideWidth); pub const PropertyFieldMap = .{ @@ -150,8 +295,17 @@ pub const BorderWidth = struct { .bottom = css.PropertyIdTag.@"border-bottom-width", .left = css.PropertyIdTag.@"border-left-width", }; + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } }; +// TODO: fallbacks /// A value for the [border-block-color](https://drafts.csswg.org/css-logical/#propdef-border-block-color) shorthand property. pub const BorderBlockColor = struct { /// The block start value. @@ -159,13 +313,22 @@ pub const BorderBlockColor = struct { /// The block end value. end: CssColor, - pub usingnamespace css.DefineShorthand(@This(), css.PropertyIdTag.@"border-block-color"); + // TODO: bring this back + // pub usingnamespace css.DefineShorthand(@This(), css.PropertyIdTag.@"border-block-color"); pub usingnamespace css.DefineSizeShorthand(@This(), CssColor); pub const PropertyFieldMap = .{ .start = css.PropertyIdTag.@"border-block-start-color", .end = css.PropertyIdTag.@"border-block-end-color", }; + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } }; /// A value for the [border-block-style](https://drafts.csswg.org/css-logical/#propdef-border-block-style) shorthand property. @@ -175,13 +338,22 @@ pub const BorderBlockStyle = struct { /// The block end value. end: LineStyle, - pub usingnamespace css.DefineShorthand(@This(), css.PropertyIdTag.@"border-block-style"); + // TODO: bring this back + // pub usingnamespace css.DefineShorthand(@This(), css.PropertyIdTag.@"border-block-style"); pub usingnamespace css.DefineSizeShorthand(@This(), LineStyle); pub const PropertyFieldMap = .{ .start = css.PropertyIdTag.@"border-block-start-style", .end = css.PropertyIdTag.@"border-block-end-style", }; + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } }; /// A value for the [border-block-width](https://drafts.csswg.org/css-logical/#propdef-border-block-width) shorthand property. @@ -191,15 +363,25 @@ pub const BorderBlockWidth = struct { /// The block end value. end: BorderSideWidth, - pub usingnamespace css.DefineShorthand(@This(), css.PropertyIdTag.@"border-block-width"); + // TODO: bring this back + // pub usingnamespace css.DefineShorthand(@This(), css.PropertyIdTag.@"border-block-width"); pub usingnamespace css.DefineSizeShorthand(@This(), BorderSideWidth); pub const PropertyFieldMap = .{ .start = css.PropertyIdTag.@"border-block-start-width", .end = css.PropertyIdTag.@"border-block-end-width", }; + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } }; +// TODO: fallbacks /// A value for the [border-inline-color](https://drafts.csswg.org/css-logical/#propdef-border-inline-color) shorthand property. pub const BorderInlineColor = struct { /// The inline start value. @@ -207,13 +389,22 @@ pub const BorderInlineColor = struct { /// The inline end value. end: CssColor, - pub usingnamespace css.DefineShorthand(@This(), css.PropertyIdTag.@"border-inline-color"); + // TODO: bring this back + // pub usingnamespace css.DefineShorthand(@This(), css.PropertyIdTag.@"border-inline-color"); pub usingnamespace css.DefineSizeShorthand(@This(), CssColor); pub const PropertyFieldMap = .{ .start = css.PropertyIdTag.@"border-inline-start-color", .end = css.PropertyIdTag.@"border-inline-end-color", }; + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } }; /// A value for the [border-inline-style](https://drafts.csswg.org/css-logical/#propdef-border-inline-style) shorthand property. @@ -223,13 +414,22 @@ pub const BorderInlineStyle = struct { /// The inline end value. end: LineStyle, - pub usingnamespace css.DefineShorthand(@This(), css.PropertyIdTag.@"border-inline-style"); + // TODO: bring this back + // pub usingnamespace css.DefineShorthand(@This(), css.PropertyIdTag.@"border-inline-style"); pub usingnamespace css.DefineSizeShorthand(@This(), LineStyle); pub const PropertyFieldMap = .{ .start = css.PropertyIdTag.@"border-inline-start-style", .end = css.PropertyIdTag.@"border-inline-end-style", }; + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } }; /// A value for the [border-inline-width](https://drafts.csswg.org/css-logical/#propdef-border-inline-width) shorthand property. @@ -239,11 +439,20 @@ pub const BorderInlineWidth = struct { /// The inline end value. end: BorderSideWidth, - pub usingnamespace css.DefineShorthand(@This(), css.PropertyIdTag.@"border-inline-width"); + // TODO: bring this back + // pub usingnamespace css.DefineShorthand(@This(), css.PropertyIdTag.@"border-inline-width"); pub usingnamespace css.DefineSizeShorthand(@This(), BorderSideWidth); pub const PropertyFieldMap = .{ .start = css.PropertyIdTag.@"border-inline-start-width", .end = css.PropertyIdTag.@"border-inline-end-width", }; + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } }; diff --git a/src/css/properties/border_image.zig b/src/css/properties/border_image.zig index 38d34a14c5..bde899c8ee 100644 --- a/src/css/properties/border_image.zig +++ b/src/css/properties/border_image.zig @@ -23,6 +23,7 @@ const Ratio = css.css_values.ratio.Ratio; const Length = css.css_values.length.LengthValue; const Rect = css.css_values.rect.Rect; const NumberOrPercentage = css.css_values.percentage.NumberOrPercentage; +const Percentage = css.css_values.percentage.Percentage; /// A value for the [border-image](https://www.w3.org/TR/css-backgrounds-3/#border-image) shorthand property. pub const BorderImage = struct { @@ -55,13 +56,15 @@ pub const BorderImage = struct { .repeat = true, }; - pub fn parse(input: *css.Parser) css.Result(BorderImageRepeat) { - _ = input; // autofix - @panic(css.todo_stuff.depth); + pub fn parse(input: *css.Parser) css.Result(BorderImage) { + return parseWithCallback(input, {}, struct { + pub fn cb(_: void, _: *css.Parser) bool { + return false; + } + }.cb); } - pub fn parseWithCallback(input: *css.Parser, comptime callback: anytype) css.Result(BorderImageRepeat) { - _ = callback; // autofix + pub fn parseWithCallback(input: *css.Parser, ctx: anytype, comptime callback: anytype) css.Result(BorderImage) { var source: ?Image = null; var slice: ?BorderImageSlice = null; var width: ?Rect(BorderImageSideWidth) = null; @@ -70,12 +73,12 @@ pub const BorderImage = struct { while (true) { if (slice == null) { - if (input.tryParse(BorderImageSlice.parse, .{})) |value| { + if (input.tryParse(BorderImageSlice.parse, .{}).asValue()) |value| { slice = value; // Parse border image width and outset, if applicable. const maybe_width_outset = input.tryParse(struct { pub fn parse(i: *css.Parser) css.Result(struct { ?Rect(BorderImageSideWidth), ?Rect(LengthOrNumber) }) { - if (input.expectDelim('/').asErr()) |e| return .{ .err = e }; + if (i.expectDelim('/').asErr()) |e| return .{ .err = e }; const w = i.tryParse(Rect(BorderImageSideWidth).parse, .{}).asValue(); @@ -84,12 +87,12 @@ pub const BorderImage = struct { if (in.expectDelim('/').asErr()) |e| return .{ .err = e }; return Rect(LengthOrNumber).parse(in); } - }.parseFn).asValue(); + }.parseFn, .{}).asValue(); - if (w == null and o == null) return .{ .err = input.newCustomError(css.ParserError.invalid_declaration) }; - return .{ .result = .{ w, 0 } }; + if (w == null and o == null) return .{ .err = i.newCustomError(css.ParserError.invalid_declaration) }; + return .{ .result = .{ w, o } }; } - }.parseFn, .{}); + }.parse, .{}); if (maybe_width_outset.asValue()) |val| { width = val[0]; @@ -112,7 +115,91 @@ pub const BorderImage = struct { continue; } } + + if (@call(.auto, callback, .{ ctx, input })) { + continue; + } + + break; } + + if (source != null or slice != null or width != null or outset != null or repeat != null) { + return .{ + .result = BorderImage{ + .source = source orelse Image.default(), + .slice = slice orelse BorderImageSlice.default(), + .width = width orelse Rect(BorderImageSideWidth).all(BorderImageSideWidth.default()), + .outset = outset orelse Rect(LengthOrNumber).all(LengthOrNumber.default()), + .repeat = repeat orelse BorderImageRepeat.default(), + }, + }; + } + return .{ .err = input.newCustomError(css.ParserError.invalid_declaration) }; + } + + pub fn toCss(this: *const BorderImage, comptime W: type, dest: *css.Printer(W)) PrintErr!void { + return toCssInternal(&this.source, &this.slice, &this.width, &this.outset, &this.repeat, W, dest); + } + + pub fn toCssInternal( + source: *const Image, + slice: *const BorderImageSlice, + width: *const Rect(BorderImageSideWidth), + outset: *const Rect(LengthOrNumber), + repeat: *const BorderImageRepeat, + comptime W: type, + dest: *css.Printer(W), + ) PrintErr!void { + if (!css.generic.eql(Image, source, &Image.default())) { + try source.toCss(W, dest); + } + const has_slice = !css.generic.eql(BorderImageSlice, slice, &BorderImageSlice.default()); + const has_width = !css.generic.eql(Rect(BorderImageSideWidth), width, &Rect(BorderImageSideWidth).all(BorderImageSideWidth.default())); + const has_outset = !css.generic.eql(Rect(LengthOrNumber), outset, &Rect(LengthOrNumber).all(LengthOrNumber{ .number = 0.0 })); + if (has_slice or has_width or has_outset) { + try dest.writeStr(" "); + try slice.toCss(W, dest); + if (has_width or has_outset) { + try dest.delim('/', true); + } + if (has_width) { + try width.toCss(W, dest); + } + + if (has_outset) { + try dest.delim('/', true); + try outset.toCss(W, dest); + } + } + + if (!css.generic.eql(BorderImageRepeat, repeat, &BorderImageRepeat.default())) { + try dest.writeStr(" "); + return repeat.toCss(W, dest); + } + + return; + } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } + + pub fn eql(this: *const BorderImage, other: *const BorderImage) bool { + return this.source.eql(&other.source) and + this.slice.eql(&other.slice) and + this.width.eql(&other.width) and + this.outset.eql(&other.outset) and + this.repeat.eql(&other.repeat); + } + + pub fn default() BorderImage { + return BorderImage{ + .source = Image.default(), + .slice = BorderImageSlice.default(), + .width = Rect(BorderImageSideWidth).all(BorderImageSideWidth.default()), + .outset = Rect(LengthOrNumber).all(LengthOrNumber.default()), + .repeat = BorderImageRepeat.default(), + }; } }; @@ -142,6 +229,21 @@ pub const BorderImageRepeat = struct { try this.vertical.toCss(W, dest); } } + + pub fn default() BorderImageRepeat { + return BorderImageRepeat{ + .horizontal = BorderImageRepeatKeyword.stretch, + .vertical = BorderImageRepeatKeyword.stretch, + }; + } + + pub fn eql(this: *const BorderImageRepeat, other: *const BorderImageRepeat) bool { + return this.horizontal.eql(&other.horizontal) and this.vertical.eql(&other.vertical); + } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } }; /// A value for the [border-image-width](https://www.w3.org/TR/css-backgrounds-3/#border-image-width) property. @@ -156,6 +258,14 @@ pub const BorderImageSideWidth = union(enum) { pub usingnamespace css.DeriveParse(@This()); pub usingnamespace css.DeriveToCss(@This()); + pub fn default() BorderImageSideWidth { + return .{ .number = 1.0 }; + } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } + pub fn eql(this: *const BorderImageSideWidth, other: *const BorderImageSideWidth) bool { return switch (this.*) { .number => |*a| switch (other.*) { @@ -219,4 +329,19 @@ pub const BorderImageSlice = struct { try dest.writeStr(" fill"); } } + + pub fn eql(this: *const BorderImageSlice, other: *const BorderImageSlice) bool { + return this.offsets.eql(&other.offsets) and this.fill == other.fill; + } + + pub fn default() BorderImageSlice { + return BorderImageSlice{ + .offsets = Rect(NumberOrPercentage).all(NumberOrPercentage{ .percentage = Percentage{ .v = 1.0 } }), + .fill = false, + }; + } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } }; diff --git a/src/css/properties/border_radius.zig b/src/css/properties/border_radius.zig index 8172ad473b..befd591f75 100644 --- a/src/css/properties/border_radius.zig +++ b/src/css/properties/border_radius.zig @@ -98,4 +98,12 @@ pub const BorderRadius = struct { try heights.toCss(W, dest); } } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } }; diff --git a/src/css/properties/box_shadow.zig b/src/css/properties/box_shadow.zig index d1255f6d3a..687643b8a7 100644 --- a/src/css/properties/box_shadow.zig +++ b/src/css/properties/box_shadow.zig @@ -37,4 +37,95 @@ pub const BoxShadow = struct { spread: Length, /// Whether the shadow is inset within the box. inset: bool, + + pub fn parse(input: *css.Parser) css.Result(@This()) { + var color: ?CssColor = null; + const Lengths = struct { x: Length, y: Length, blur: Length, spread: Length }; + var lengths: ?Lengths = null; + var inset = false; + + while (true) { + if (!inset) { + if (input.tryParse(css.Parser.expectIdentMatching, .{"inset"}).isOk()) { + inset = true; + continue; + } + } + + if (lengths == null) { + const value = input.tryParse(struct { + fn parse(p: *css.Parser) css.Result(Lengths) { + const horizontal = switch (Length.parse(p)) { + .result => |v| v, + .err => |e| return .{ .err = e }, + }; + const vertical = switch (Length.parse(p)) { + .result => |v| v, + .err => |e| return .{ .err = e }, + }; + const blur = p.tryParse(Length.parse, .{}).asValue() orelse Length.zero(); + const spread = p.tryParse(Length.parse, .{}).asValue() orelse Length.zero(); + return .{ .result = .{ .x = horizontal, .y = vertical, .blur = blur, .spread = spread } }; + } + }.parse, .{}); + + if (value.isOk()) { + lengths = value.result; + continue; + } + } + + if (color == null) { + if (input.tryParse(CssColor.parse, .{}).isOk()) { + color = input.tryParse(CssColor.parse, .{}).result; + continue; + } + } + + break; + } + + const final_lengths = lengths orelse return .{ .err = input.newError(.qualified_rule_invalid) }; + return .{ .result = BoxShadow{ + .color = color orelse CssColor{ .current_color = {} }, + .x_offset = final_lengths.x, + .y_offset = final_lengths.y, + .blur = final_lengths.blur, + .spread = final_lengths.spread, + .inset = inset, + } }; + } + + pub fn toCss(this: *const @This(), comptime W: type, dest: *css.Printer(W)) css.PrintErr!void { + if (this.inset) { + try dest.writeStr("inset "); + } + + try this.x_offset.toCss(W, dest); + try dest.writeChar(' '); + try this.y_offset.toCss(W, dest); + + if (!this.blur.eql(&Length.zero()) or !this.spread.eql(&Length.zero())) { + try dest.writeChar(' '); + try this.blur.toCss(W, dest); + + if (!this.spread.eql(&Length.zero())) { + try dest.writeChar(' '); + try this.spread.toCss(W, dest); + } + } + + if (!this.color.eql(&CssColor{ .current_color = {} })) { + try dest.writeChar(' '); + try this.color.toCss(W, dest); + } + } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } }; diff --git a/src/css/properties/css_modules.zig b/src/css/properties/css_modules.zig index 037ab90f73..fa087a3866 100644 --- a/src/css/properties/css_modules.zig +++ b/src/css/properties/css_modules.zig @@ -46,7 +46,7 @@ pub const Composes = struct { pub fn toCss(this: *const @This(), comptime W: type, dest: *Printer(W)) PrintErr!void { var first = true; - for (this.names.items) |name| { + for (this.names.slice()) |name| { if (first) { first = false; } else { @@ -60,6 +60,14 @@ pub const Composes = struct { try from.toCss(W, dest); } } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } }; /// Defines where the class names referenced in the `composes` property are located. @@ -73,6 +81,10 @@ pub const Specifier = union(enum) { /// The referenced name comes from a source index (used during bundling). source_index: u32, + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } + pub fn parse(input: *css.Parser) css.Result(Specifier) { if (input.tryParse(css.Parser.expectString, .{}).asValue()) |file| { return .{ .result = .{ .file = file } }; @@ -88,4 +100,12 @@ pub const Specifier = union(enum) { .source_index => {}, }; } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } + + pub fn hash(this: *const @This(), hasher: *std.hash.Wyhash) void { + return css.implementHash(@This(), this, hasher); + } }; diff --git a/src/css/properties/custom.zig b/src/css/properties/custom.zig index 1c9eeba9ea..7f72cf2195 100644 --- a/src/css/properties/custom.zig +++ b/src/css/properties/custom.zig @@ -41,12 +41,6 @@ pub const TokenList = struct { const This = @This(); - pub fn deepClone(this: *const TokenList, allocator: Allocator) TokenList { - return .{ - .v = css.deepClone(TokenOrValue, allocator, &this.v), - }; - } - pub fn deinit(this: *TokenList, allocator: Allocator) void { for (this.v.items) |*token_or_value| { token_or_value.deinit(allocator); @@ -603,6 +597,20 @@ pub const TokenList = struct { return .{ .result = {} }; } + + pub fn eql(lhs: *const TokenList, rhs: *const TokenList) bool { + return css.generic.eqlList(TokenOrValue, &lhs.v, &rhs.v); + } + + pub fn hash(this: *const @This(), hasher: *std.hash.Wyhash) void { + return css.implementHash(@This(), this, hasher); + } + + pub fn deepClone(this: *const TokenList, allocator: Allocator) TokenList { + return .{ + .v = css.deepClone(TokenOrValue, allocator, &this.v), + }; + } }; pub const TokenListFns = TokenList; @@ -621,6 +629,10 @@ pub const UnresolvedColor = union(enum) { b: f32, /// The unresolved alpha component. alpha: TokenList, + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } + pub fn __generateHash() void {} }, /// An hsl() color. HSL: struct { @@ -632,6 +644,10 @@ pub const UnresolvedColor = union(enum) { l: f32, /// The unresolved alpha component. alpha: TokenList, + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } + pub fn __generateHash() void {} }, /// The light-dark() function. light_dark: struct { @@ -639,9 +655,23 @@ pub const UnresolvedColor = union(enum) { light: TokenList, /// The dark value. dark: TokenList, + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } + + pub fn __generateHash() void {} }, const This = @This(); + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } + + pub fn hash(this: *const @This(), hasher: *std.hash.Wyhash) void { + return css.implementHash(@This(), this, hasher); + } + pub fn deepClone(this: *const This, allocator: Allocator) This { return switch (this.*) { .RGB => |*rgb| .{ .RGB = .{ .r = rgb.r, .g = rgb.g, .b = rgb.b, .alpha = rgb.alpha.deepClone(allocator) } }, @@ -893,6 +923,14 @@ pub const Variable = struct { const This = @This(); + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } + + pub fn hash(this: *const @This(), hasher: *std.hash.Wyhash) void { + return css.implementHash(@This(), this, hasher); + } + pub fn deepClone(this: *const Variable, allocator: Allocator) Variable { return .{ .name = this.name, @@ -953,6 +991,14 @@ pub const EnvironmentVariable = struct { /// A fallback value in case the variable is not defined. fallback: ?TokenList, + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } + + pub fn hash(this: *const @This(), hasher: *std.hash.Wyhash) void { + return css.implementHash(@This(), this, hasher); + } + pub fn deepClone(this: *const EnvironmentVariable, allocator: Allocator) EnvironmentVariable { return .{ .name = this.name, @@ -1047,6 +1093,13 @@ pub const EnvironmentVariableName = union(enum) { /// An unknown environment variable. unknown: CustomIdent, + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } + pub fn hash(this: *const @This(), hasher: *std.hash.Wyhash) void { + return css.implementHash(@This(), this, hasher); + } + pub fn parse(input: *css.Parser) Result(EnvironmentVariableName) { if (input.tryParse(UAEnvironmentVariable.parse, .{}).asValue()) |ua| { return .{ .result = .{ .ua = ua } }; @@ -1101,6 +1154,10 @@ pub const UAEnvironmentVariable = enum { @"viewport-segment-right", pub usingnamespace css.DefineEnumProperty(@This()); + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } }; /// A custom CSS function. @@ -1112,6 +1169,14 @@ pub const Function = struct { const This = @This(); + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } + + pub fn hash(this: *const @This(), hasher: *std.hash.Wyhash) void { + return css.implementHash(@This(), this, hasher); + } + pub fn deepClone(this: *const Function, allocator: Allocator) Function { return .{ .name = this.name, @@ -1165,6 +1230,14 @@ pub const TokenOrValue = union(enum) { /// An animation name. animation_name: AnimationName, + pub fn eql(lhs: *const TokenOrValue, rhs: *const TokenOrValue) bool { + return css.implementEql(TokenOrValue, lhs, rhs); + } + + pub fn hash(this: *const @This(), hasher: *std.hash.Wyhash) void { + return css.implementHash(@This(), this, hasher); + } + pub fn deepClone(this: *const TokenOrValue, allocator: Allocator) TokenOrValue { return switch (this.*) { .token => this.*, @@ -1233,6 +1306,10 @@ pub const UnparsedProperty = struct { return .{ .result = .{ .property_id = property_id, .value = value } }; } + + pub fn deepClone(this: *const @This(), allocator: Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } }; /// A CSS custom property, representing any unknown property. @@ -1273,6 +1350,14 @@ pub const CustomProperty = struct { .value = value, } }; } + + pub fn deepClone(this: *const @This(), allocator: Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } }; /// A CSS custom property name. @@ -1300,6 +1385,14 @@ pub const CustomPropertyName = union(enum) { .unknown => |unknown| return unknown.v, } } + + pub fn deepClone(this: *const @This(), allocator: Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } }; pub fn tryParseColorToken(f: []const u8, state: *const css.ParserState, input: *css.Parser) ?CssColor { diff --git a/src/css/properties/display.zig b/src/css/properties/display.zig index a469a74a9a..251c001b97 100644 --- a/src/css/properties/display.zig +++ b/src/css/properties/display.zig @@ -33,6 +33,21 @@ pub const Display = union(enum) { keyword: DisplayKeyword, /// The inside and outside display values. pair: DisplayPair, + + pub usingnamespace css.DeriveParse(@This()); + pub usingnamespace css.DeriveToCss(@This()); + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } + + pub fn hash(this: *const @This(), hasher: *std.hash.Wyhash) void { + return css.implementHash(@This(), this, hasher); + } + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } }; /// A value for the [visibility](https://drafts.csswg.org/css-display-3/#visibility) property. @@ -79,6 +94,128 @@ pub const DisplayPair = struct { inside: DisplayInside, /// Whether this is a list item. is_list_item: bool, + + pub fn parse(input: *css.Parser) css.Result(@This()) { + var list_item = false; + var outside: ?DisplayOutside = null; + var inside: ?DisplayInside = null; + + while (true) { + if (input.tryParse(css.Parser.expectIdentMatching, .{"list-item"}).isOk()) { + list_item = true; + continue; + } + + if (outside == null) { + if (input.tryParse(DisplayOutside.parse, .{}).asValue()) |o| { + outside = o; + continue; + } + } + + if (inside == null) { + if (input.tryParse(DisplayInside.parse, .{}).asValue()) |i| { + inside = i; + continue; + } + } + + break; + } + + if (list_item or inside != null or outside != null) { + const final_inside: DisplayInside = inside orelse DisplayInside.flow; + const final_outside: DisplayOutside = outside orelse switch (final_inside) { + // "If is omitted, the element’s outside display type + // defaults to block — except for ruby, which defaults to inline." + // https://drafts.csswg.org/css-display/#inside-model + .ruby => .@"inline", + else => .block, + }; + + if (list_item and !(final_inside == .flow or final_inside == .flow_root)) { + return .{ .err = input.newCustomError(.invalid_declaration) }; + } + + return .{ .result = .{ + .outside = final_outside, + .inside = final_inside, + .is_list_item = list_item, + } }; + } + + const location = input.currentSourceLocation(); + const ident = switch (input.expectIdent()) { + .result => |v| v, + .err => |e| return .{ .err = e }, + }; + + const displayIdentMap = bun.ComptimeStringMap(DisplayPair, .{ + .{ "inline-block", DisplayPair{ .outside = .@"inline", .inside = .flow_root, .is_list_item = false } }, + .{ "inline-table", DisplayPair{ .outside = .@"inline", .inside = .table, .is_list_item = false } }, + .{ "inline-flex", DisplayPair{ .outside = .@"inline", .inside = .{ .flex = css.VendorPrefix{ .none = true } }, .is_list_item = false } }, + .{ "-webkit-inline-flex", DisplayPair{ .outside = .@"inline", .inside = .{ .flex = css.VendorPrefix{ .webkit = true } }, .is_list_item = false } }, + .{ "-ms-inline-flexbox", DisplayPair{ .outside = .@"inline", .inside = .{ .flex = css.VendorPrefix{ .ms = true } }, .is_list_item = false } }, + .{ "-webkit-inline-box", DisplayPair{ .outside = .@"inline", .inside = .{ .box = css.VendorPrefix{ .webkit = true } }, .is_list_item = false } }, + .{ "-moz-inline-box", DisplayPair{ .outside = .@"inline", .inside = .{ .box = css.VendorPrefix{ .moz = true } }, .is_list_item = false } }, + .{ "inline-grid", DisplayPair{ .outside = .@"inline", .inside = .grid, .is_list_item = false } }, + }); + if (displayIdentMap.get(ident)) |pair| { + return .{ .result = pair }; + } + + return .{ .err = location.newUnexpectedTokenError(.{ .ident = ident }) }; + } + + pub fn toCss(this: *const DisplayPair, comptime W: type, dest: *css.Printer(W)) css.PrintErr!void { + if (this.outside == .@"inline" and this.inside == .flow_root and !this.is_list_item) { + return dest.writeStr("inline-block"); + } else if (this.outside == .@"inline" and this.inside == .table and !this.is_list_item) { + return dest.writeStr("inline-table"); + } else if (this.outside == .@"inline" and this.inside == .flex and !this.is_list_item) { + try this.inside.flex.toCss(W, dest); + if (this.inside.flex.eql(css.VendorPrefix{ .ms = true })) { + return dest.writeStr("inline-flexbox"); + } else { + return dest.writeStr("inline-flex"); + } + } else if (this.outside == .@"inline" and this.inside == .box and !this.is_list_item) { + try this.inside.box.toCss(W, dest); + return dest.writeStr("inline-box"); + } else if (this.outside == .@"inline" and this.inside == .grid and !this.is_list_item) { + return dest.writeStr("inline-grid"); + } else { + const default_outside: DisplayOutside = switch (this.inside) { + .ruby => .@"inline", + else => .block, + }; + + var needs_space = false; + if (!this.outside.eql(&default_outside) or (this.inside.eql(&DisplayInside{ .flow = {} }) and !this.is_list_item)) { + try this.outside.toCss(W, dest); + needs_space = true; + } + + if (!this.inside.eql(&DisplayInside{ .flow = {} })) { + if (needs_space) { + try dest.writeChar(' '); + } + try this.inside.toCss(W, dest); + needs_space = true; + } + + if (this.is_list_item) { + if (needs_space) { + try dest.writeChar(' '); + } + try dest.writeStr("list-item"); + } + } + } + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } }; /// A [``](https://drafts.csswg.org/css-display-3/#typedef-display-outside) value. @@ -99,4 +236,57 @@ pub const DisplayInside = union(enum) { box: css.VendorPrefix, grid, ruby, + + pub fn parse(input: *css.Parser) css.Result(@This()) { + const displayInsideMap = bun.ComptimeStringMap(DisplayInside, .{ + .{ "flow", DisplayInside.flow }, + .{ "flow-root", DisplayInside.flow_root }, + .{ "table", .table }, + .{ "flex", .{ .flex = css.VendorPrefix{ .none = true } } }, + .{ "-webkit-flex", .{ .flex = css.VendorPrefix{ .webkit = true } } }, + .{ "-ms-flexbox", .{ .flex = css.VendorPrefix{ .ms = true } } }, + .{ "-webkit-box", .{ .box = css.VendorPrefix{ .webkit = true } } }, + .{ "-moz-box", .{ .box = css.VendorPrefix{ .moz = true } } }, + .{ "grid", .grid }, + .{ "ruby", .ruby }, + }); + + const location = input.currentSourceLocation(); + const ident = switch (input.expectIdent()) { + .result => |v| v, + .err => |e| return .{ .err = e }, + }; + + if (displayInsideMap.get(ident)) |value| { + return .{ .result = value }; + } + + return .{ .err = location.newUnexpectedTokenError(.{ .ident = ident }) }; + } + + pub fn toCss(this: *const DisplayInside, comptime W: type, dest: *css.Printer(W)) css.PrintErr!void { + switch (this.*) { + .flow => try dest.writeStr("flow"), + .flow_root => try dest.writeStr("flow-root"), + .table => try dest.writeStr("table"), + .flex => |prefix| { + try prefix.toCss(W, dest); + if (prefix.eql(css.VendorPrefix{ .ms = true })) { + try dest.writeStr("flexbox"); + } else { + try dest.writeStr("flex"); + } + }, + .box => |prefix| { + try prefix.toCss(W, dest); + try dest.writeStr("box"); + }, + .grid => try dest.writeStr("grid"), + .ruby => try dest.writeStr("ruby"), + } + } + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } }; diff --git a/src/css/properties/flex.zig b/src/css/properties/flex.zig index ffd283a680..c94bfeb637 100644 --- a/src/css/properties/flex.zig +++ b/src/css/properties/flex.zig @@ -12,6 +12,7 @@ const Error = css.Error; const ContainerName = css.css_rules.container.ContainerName; +const CSSNumberFns = css.css_values.number.CSSNumberFns; const LengthPercentage = css.css_values.length.LengthPercentage; const CustomIdent = css.css_values.ident.CustomIdent; const CSSString = css.css_values.string.CSSString; @@ -30,46 +31,365 @@ const Angle = css.css_values.angle.Angle; const Url = css.css_values.url.Url; /// A value for the [flex-direction](https://www.w3.org/TR/2018/CR-css-flexbox-1-20181119/#propdef-flex-direction) property. -pub const FlexDirection = css.DefineEnumProperty(@compileError(css.todo_stuff.depth)); +/// A value for the [flex-direction](https://www.w3.org/TR/2018/CR-css-flexbox-1-20181119/#propdef-flex-direction) property. +pub const FlexDirection = enum { + /// Flex items are laid out in a row. + row, + /// Flex items are laid out in a row, and reversed. + @"row-reverse", + /// Flex items are laid out in a column. + column, + /// Flex items are laid out in a column, and reversed. + @"column-reverse", + + pub usingnamespace css.DefineEnumProperty(@This()); + + pub fn default() FlexDirection { + return .row; + } +}; /// A value for the [flex-wrap](https://www.w3.org/TR/2018/CR-css-flexbox-1-20181119/#flex-wrap-property) property. -pub const FlexWrap = css.DefineEnumProperty(@compileError(css.todo_stuff.depth)); +/// A value for the [flex-wrap](https://www.w3.org/TR/2018/CR-css-flexbox-1-20181119/#flex-wrap-property) property. +pub const FlexWrap = enum { + /// The flex items do not wrap. + nowrap, + /// The flex items wrap. + wrap, + /// The flex items wrap, in reverse. + @"wrap-reverse", + + pub usingnamespace css.DefineEnumProperty(@This()); + + pub fn default() FlexWrap { + return .nowrap; + } +}; /// A value for the [flex-flow](https://www.w3.org/TR/2018/CR-css-flexbox-1-20181119/#flex-flow-property) shorthand property. -pub const FlexFlow = css.DefineEnumProperty(@compileError(css.todo_stuff.depth)); +/// A value for the [flex-flow](https://www.w3.org/TR/2018/CR-css-flexbox-1-20181119/#flex-flow-property) shorthand property. +pub const FlexFlow = struct { + /// The direction that flex items flow. + direction: FlexDirection, + /// How the flex items wrap. + wrap: FlexWrap, + + pub usingnamespace css.DefineShorthand(@This(), css.PropertyIdTag.@"flex-flow"); + + pub const PropertyFieldMap = .{ + .direction = css.PropertyIdTag.@"flex-direction", + .wrap = css.PropertyIdTag.@"flex-wrap", + }; + + pub const VendorPrefixMap = .{ + .direction = true, + .wrap = true, + }; + + pub fn parse(input: *css.Parser) css.Result(@This()) { + var direction: ?FlexDirection = null; + var wrap: ?FlexWrap = null; + + while (true) { + if (direction == null) { + if (input.tryParse(FlexDirection.parse, .{}).asValue()) |value| { + direction = value; + continue; + } + } + if (wrap == null) { + if (input.tryParse(FlexWrap.parse, .{}).asValue()) |value| { + wrap = value; + continue; + } + } + break; + } + + return .{ + .result = FlexFlow{ + .direction = direction orelse FlexDirection.row, + .wrap = wrap orelse FlexWrap.nowrap, + }, + }; + } + + pub fn toCss(this: *const FlexFlow, comptime W: type, dest: *css.Printer(W)) css.PrintErr!void { + var needs_space = false; + if (!this.direction.eql(&FlexDirection.default()) or this.wrap.eql(&FlexWrap.default())) { + try this.direction.toCss(W, dest); + needs_space = true; + } + + if (!this.wrap.eql(&FlexWrap.default())) { + if (needs_space) { + try dest.writeStr(" "); + } + try this.wrap.toCss(W, dest); + } + + return; + } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } +}; /// A value for the [flex](https://www.w3.org/TR/2018/CR-css-flexbox-1-20181119/#flex-property) shorthand property. -pub const Flex = css.DefineEnumProperty(@compileError(css.todo_stuff.depth)); +/// A value for the [flex](https://www.w3.org/TR/2018/CR-css-flexbox-1-20181119/#flex-property) shorthand property. +pub const Flex = struct { + /// The flex grow factor. + grow: CSSNumber, + /// The flex shrink factor. + shrink: CSSNumber, + /// The flex basis. + basis: LengthPercentageOrAuto, + + pub usingnamespace css.DefineShorthand(@This(), css.PropertyIdTag.flex); + + pub const PropertyFieldMap = .{ + .grow = css.PropertyIdTag.@"flex-grow", + .shrink = css.PropertyIdTag.@"flex-shrink", + .basis = css.PropertyIdTag.@"flex-basis", + }; + + pub const VendorPrefixMap = .{ + .grow = true, + .shrink = true, + .basis = true, + }; + + pub fn parse(input: *css.Parser) css.Result(@This()) { + if (input.tryParse(css.Parser.expectIdentMatching, .{"none"}).isOk()) { + return .{ + .result = .{ + .grow = 0.0, + .shrink = 0.0, + .basis = LengthPercentageOrAuto.auto, + }, + }; + } + + var grow: ?CSSNumber = null; + var shrink: ?CSSNumber = null; + var basis: ?LengthPercentageOrAuto = null; + + while (true) { + if (grow == null) { + if (input.tryParse(CSSNumberFns.parse, .{}).asValue()) |value| { + grow = value; + shrink = input.tryParse(CSSNumberFns.parse, .{}).asValue(); + continue; + } + } + + if (basis == null) { + if (input.tryParse(LengthPercentageOrAuto.parse, .{}).asValue()) |value| { + basis = value; + continue; + } + } + + break; + } + + return .{ + .result = .{ + .grow = grow orelse 1.0, + .shrink = shrink orelse 1.0, + .basis = basis orelse LengthPercentageOrAuto{ .length = LengthPercentage{ .percentage = .{ .v = 0.0 } } }, + }, + }; + } + + pub fn toCss(this: *const Flex, comptime W: type, dest: *css.Printer(W)) css.PrintErr!void { + if (this.grow == 0.0 and this.shrink == 0.0 and this.basis == .auto) { + try dest.writeStr("none"); + return; + } + + const ZeroKind = enum { + NonZero, + Length, + Percentage, + }; + + // If the basis is unitless 0, we must write all three components to disambiguate. + // If the basis is 0%, we can omit the basis. + const basis_kind = switch (this.basis) { + .length => |lp| brk: { + if (lp == .dimension and lp.dimension.isZero()) break :brk ZeroKind.Length; + if (lp == .percentage and lp.percentage.isZero()) break :brk ZeroKind.Percentage; + break :brk ZeroKind.NonZero; + }, + else => ZeroKind.NonZero, + }; + + if (this.grow != 1.0 or this.shrink != 1.0 or basis_kind != .NonZero) { + try CSSNumberFns.toCss(&this.grow, W, dest); + if (this.shrink != 1.0 or basis_kind == .Length) { + try dest.writeStr(" "); + try CSSNumberFns.toCss(&this.shrink, W, dest); + } + } + + if (basis_kind != .Percentage) { + if (this.grow != 1.0 or this.shrink != 1.0 or basis_kind == .Length) { + try dest.writeStr(" "); + } + try this.basis.toCss(W, dest); + } + } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } +}; /// A value for the legacy (prefixed) [box-orient](https://www.w3.org/TR/2009/WD-css3-flexbox-20090723/#orientation) property. /// Partially equivalent to `flex-direction` in the standard syntax. -pub const BoxOrient = css.DefineEnumProperty(@compileError(css.todo_stuff.depth)); - /// A value for the legacy (prefixed) [box-orient](https://www.w3.org/TR/2009/WD-css3-flexbox-20090723/#orientation) property. /// Partially equivalent to `flex-direction` in the standard syntax. -pub const BoxDirection = css.DefineEnumProperty(@compileError(css.todo_stuff.depth)); +pub const BoxOrient = enum { + /// Items are laid out horizontally. + horizontal, + /// Items are laid out vertically. + vertical, + /// Items are laid out along the inline axis, according to the writing direction. + @"inline-axis", + /// Items are laid out along the block axis, according to the writing direction. + @"block-axis", + + pub usingnamespace css.DefineEnumProperty(@This()); +}; + +/// A value for the legacy (prefixed) [box-direction](https://www.w3.org/TR/2009/WD-css3-flexbox-20090723/#displayorder) property. +/// Partially equivalent to the `flex-direction` property in the standard syntax. +pub const BoxDirection = enum { + /// Items flow in the natural direction. + normal, + /// Items flow in the reverse direction. + reverse, + + pub usingnamespace css.DefineEnumProperty(@This()); +}; /// A value for the legacy (prefixed) [box-align](https://www.w3.org/TR/2009/WD-css3-flexbox-20090723/#alignment) property. /// Equivalent to the `align-items` property in the standard syntax. -pub const BoxAlign = css.DefineEnumProperty(@compileError(css.todo_stuff.depth)); +/// A value for the legacy (prefixed) [box-align](https://www.w3.org/TR/2009/WD-css3-flexbox-20090723/#alignment) property. +/// Equivalent to the `align-items` property in the standard syntax. +pub const BoxAlign = enum { + /// Items are aligned to the start. + start, + /// Items are aligned to the end. + end, + /// Items are centered. + center, + /// Items are aligned to the baseline. + baseline, + /// Items are stretched. + stretch, + + pub usingnamespace css.DefineEnumProperty(@This()); +}; /// A value for the legacy (prefixed) [box-pack](https://www.w3.org/TR/2009/WD-css3-flexbox-20090723/#packing) property. /// Equivalent to the `justify-content` property in the standard syntax. -pub const BoxPack = css.DefineEnumProperty(@compileError(css.todo_stuff.depth)); +/// A value for the legacy (prefixed) [box-pack](https://www.w3.org/TR/2009/WD-css3-flexbox-20090723/#packing) property. +/// Equivalent to the `justify-content` property in the standard syntax. +pub const BoxPack = enum { + /// Items are justified to the start. + start, + /// Items are justified to the end. + end, + /// Items are centered. + center, + /// Items are justified to the start and end. + justify, + + pub usingnamespace css.DefineEnumProperty(@This()); +}; /// A value for the legacy (prefixed) [box-lines](https://www.w3.org/TR/2009/WD-css3-flexbox-20090723/#multiple) property. /// Equivalent to the `flex-wrap` property in the standard syntax. -pub const BoxLines = css.DefineEnumProperty(@compileError(css.todo_stuff.depth)); +/// A value for the legacy (prefixed) [box-lines](https://www.w3.org/TR/2009/WD-css3-flexbox-20090723/#multiple) property. +/// Equivalent to the `flex-wrap` property in the standard syntax. +pub const BoxLines = enum { + /// Items are laid out in a single line. + single, + /// Items may wrap into multiple lines. + multiple, + + pub usingnamespace css.DefineEnumProperty(@This()); +}; // Old flex (2012): https://www.w3.org/TR/2012/WD-css3-flexbox-20120322/ /// A value for the legacy (prefixed) [flex-pack](https://www.w3.org/TR/2012/WD-css3-flexbox-20120322/#flex-pack) property. /// Equivalent to the `justify-content` property in the standard syntax. -pub const FlexPack = css.DefineEnumProperty(@compileError(css.todo_stuff.depth)); +/// A value for the legacy (prefixed) [flex-pack](https://www.w3.org/TR/2012/WD-css3-flexbox-20120322/#flex-pack) property. +/// Equivalent to the `justify-content` property in the standard syntax. +pub const FlexPack = enum { + /// Items are justified to the start. + start, + /// Items are justified to the end. + end, + /// Items are centered. + center, + /// Items are justified to the start and end. + justify, + /// Items are distributed evenly, with half size spaces on either end. + distribute, + + pub usingnamespace css.DefineEnumProperty(@This()); +}; /// A value for the legacy (prefixed) [flex-item-align](https://www.w3.org/TR/2012/WD-css3-flexbox-20120322/#flex-align) property. /// Equivalent to the `align-self` property in the standard syntax. -pub const FlexItemAlign = css.DefineEnumProperty(@compileError(css.todo_stuff.depth)); +/// A value for the legacy (prefixed) [flex-item-align](https://www.w3.org/TR/2012/WD-css3-flexbox-20120322/#flex-align) property. +/// Equivalent to the `align-self` property in the standard syntax. +pub const FlexItemAlign = enum { + /// Equivalent to the value of `flex-align`. + auto, + /// The item is aligned to the start. + start, + /// The item is aligned to the end. + end, + /// The item is centered. + center, + /// The item is aligned to the baseline. + baseline, + /// The item is stretched. + stretch, + + pub usingnamespace css.DefineEnumProperty(@This()); +}; /// A value for the legacy (prefixed) [flex-line-pack](https://www.w3.org/TR/2012/WD-css3-flexbox-20120322/#flex-line-pack) property. /// Equivalent to the `align-content` property in the standard syntax. -pub const FlexLinePack = css.DefineEnumProperty(@compileError(css.todo_stuff.depth)); +/// A value for the legacy (prefixed) [flex-line-pack](https://www.w3.org/TR/2012/WD-css3-flexbox-20120322/#flex-line-pack) property. +/// Equivalent to the `align-content` property in the standard syntax. +pub const FlexLinePack = enum { + /// Content is aligned to the start. + start, + /// Content is aligned to the end. + end, + /// Content is centered. + center, + /// Content is justified. + justify, + /// Content is distributed evenly, with half size spaces on either end. + distribute, + /// Content is stretched. + stretch, + + pub usingnamespace css.DefineEnumProperty(@This()); +}; diff --git a/src/css/properties/font.zig b/src/css/properties/font.zig index f6ef2943b1..5bb73a2abb 100644 --- a/src/css/properties/font.zig +++ b/src/css/properties/font.zig @@ -20,6 +20,7 @@ const LengthPercentageOrAuto = css_values.length.LengthPercentageOrAuto; const PropertyCategory = css.PropertyCategory; const LogicalGroup = css.LogicalGroup; const CSSNumber = css.css_values.number.CSSNumber; +const CSSNumberFns = css.css_values.number.CSSNumberFns; const CSSInteger = css.css_values.number.CSSInteger; const NumberOrPercentage = css.css_values.percentage.NumberOrPercentage; const Percentage = css.css_values.percentage.Percentage; @@ -47,30 +48,19 @@ pub const FontWeight = union(enum) { lighter, // TODO: implement this - // pub usingnamespace css.DeriveParse(@This()); - // pub usingnamespace css.DeriveToCss(@This()); + pub usingnamespace css.DeriveParse(@This()); + pub usingnamespace css.DeriveToCss(@This()); pub inline fn default() FontWeight { return .{ .absolute = AbsoluteFontWeight.default() }; } - pub fn parse(input: *css.Parser) css.Result(FontWeight) { - _ = input; // autofix - @panic(css.todo_stuff.depth); + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); } - pub fn toCss(this: *const FontWeight, comptime W: type, dest: *css.Printer(W)) css.PrintErr!void { - _ = this; // autofix - _ = dest; // autofix - @panic(css.todo_stuff.depth); - } - - pub fn eql(lhs: *const FontWeight, rhs: *const FontWeight) bool { - return switch (lhs.*) { - .absolute => rhs.* == .absolute and lhs.absolute.eql(&rhs.absolute), - .bolder => rhs.* == .bolder, - .lighter => rhs.* == .lighter, - }; + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); } }; @@ -86,6 +76,16 @@ pub const AbsoluteFontWeight = union(enum) { /// Same as `700`. bold, + pub usingnamespace css.DeriveParse(@This()); + + pub fn toCss(this: *const AbsoluteFontWeight, comptime W: type, dest: *css.Printer(W)) css.PrintErr!void { + return switch (this.*) { + .weight => |*weight| CSSNumberFns.toCss(weight, W, dest), + .normal => try dest.writeStr(if (dest.minify) "400" else "normal"), + .bold => try dest.writeStr(if (dest.minify) "700" else "bold"), + }; + } + pub inline fn default() AbsoluteFontWeight { return .normal; } @@ -108,19 +108,15 @@ pub const FontSize = union(enum) { /// A relative font size keyword. relative: RelativeFontSize, - // TODO: implement this - // pub usingnamespace css.DeriveParse(@This()); - // pub usingnamespace css.DeriveToCss(@This()); + pub usingnamespace css.DeriveParse(@This()); + pub usingnamespace css.DeriveToCss(@This()); - pub fn parse(input: *css.Parser) css.Result(FontSize) { - _ = input; // autofix - @panic(css.todo_stuff.depth); + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); } - pub fn toCss(this: *const FontSize, comptime W: type, dest: *css.Printer(W)) css.PrintErr!void { - _ = this; // autofix - _ = dest; // autofix - @panic(css.todo_stuff.depth); + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); } }; @@ -185,6 +181,10 @@ pub const FontStretch = union(enum) { return lhs.keyword == rhs.keyword and lhs.percentage.v == rhs.percentage.v; } + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } + pub inline fn default() FontStretch { return .{ .keyword = FontStretchKeyword.default() }; } @@ -297,6 +297,14 @@ pub const FontFamily = union(enum) { }, } } + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } }; /// A [generic font family](https://www.w3.org/TR/css-fonts-4/#generic-font-families) name, @@ -370,14 +378,14 @@ pub const FontStyle = union(enum) { } pub fn toCss(this: *const FontStyle, comptime W: type, dest: *Printer(W)) PrintErr!void { - switch (this) { + switch (this.*) { .normal => try dest.writeStr("normal"), .italic => try dest.writeStr("italic"), .oblique => |angle| { try dest.writeStr("oblique"); - if (angle != FontStyle.defaultObliqueAngle()) { + if (angle.eql(&FontStyle.defaultObliqueAngle())) { try dest.writeChar(' '); - try angle.toCss(dest); + try angle.toCss(W, dest); } }, } @@ -386,6 +394,14 @@ pub const FontStyle = union(enum) { pub fn defaultObliqueAngle() Angle { return Angle{ .deg = 14.0 }; } + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } }; /// A value for the [font-variant-caps](https://www.w3.org/TR/css-fonts-4/#font-variant-caps-prop) property. @@ -419,11 +435,14 @@ pub const FontVariantCaps = enum { } pub fn parseCss2(input: *css.Parser) css.Result(FontVariantCaps) { - const value = try FontVariantCaps.parse(input); + const value = switch (FontVariantCaps.parse(input)) { + .result => |v| v, + .err => |e| return .{ .err = e }, + }; if (!value.isCss2()) { return .{ .err = input.newCustomError(css.ParserError.invalid_value) }; } - return value; + return .{ .result = value }; } }; @@ -436,18 +455,15 @@ pub const LineHeight = union(enum) { /// An explicit height. length: LengthPercentage, - // pub usingnamespace css.DeriveParse(@This()); - // pub usingnamespace css.DeriveToCss(@This()); + pub usingnamespace @call(.auto, css.DeriveParse, .{@This()}); + pub usingnamespace @call(.auto, css.DeriveToCss, .{@This()}); - pub fn parse(input: *css.Parser) css.Result(LineHeight) { - _ = input; // autofix - @panic(css.todo_stuff.depth); + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); } - pub fn toCss(this: *const LineHeight, comptime W: type, dest: *css.Printer(W)) css.PrintErr!void { - _ = this; // autofix - _ = dest; // autofix - @panic(css.todo_stuff.depth); + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); } pub fn default() LineHeight { @@ -458,7 +474,7 @@ pub const LineHeight = union(enum) { /// A value for the [font](https://www.w3.org/TR/css-fonts-4/#font-prop) shorthand property. pub const Font = struct { /// The font family. - family: ArrayList(FontFamily), + family: bun.BabyList(FontFamily), /// The font size. size: FontSize, /// The font style. @@ -472,7 +488,17 @@ pub const Font = struct { /// How the text should be capitalized. Only CSS 2.1 values are supported. variant_caps: FontVariantCaps, - pub usingnamespace css.DefineShorthand(@This()); + pub usingnamespace css.DefineShorthand(@This(), css.PropertyIdTag.font); + + pub const PropertyFieldMap = .{ + .family = css.PropertyIdTag.@"font-family", + .size = css.PropertyIdTag.@"font-size", + .style = css.PropertyIdTag.@"font-style", + .weight = css.PropertyIdTag.@"font-weight", + .stretch = css.PropertyIdTag.@"font-stretch", + .line_height = css.PropertyIdTag.@"line-height", + .variant_caps = css.PropertyIdTag.@"font-variant-caps", + }; pub fn parse(input: *css.Parser) css.Result(Font) { var style: ?FontStyle = null; @@ -490,7 +516,7 @@ pub const Font = struct { } if (style == null) { - if (input.tryParse(FontStyle.parse, .{})) |value| { + if (input.tryParse(FontStyle.parse, .{}).asValue()) |value| { style = value; count += 1; continue; @@ -498,7 +524,7 @@ pub const Font = struct { } if (weight == null) { - if (input.tryParse(FontWeight.parse, .{})) |value| { + if (input.tryParse(FontWeight.parse, .{}).asValue()) |value| { weight = value; count += 1; continue; @@ -506,7 +532,7 @@ pub const Font = struct { } if (variant_caps != null) { - if (input.tryParse(FontVariantCaps.parseCss2, .{})) |value| { + if (input.tryParse(FontVariantCaps.parseCss2, .{}).asValue()) |value| { variant_caps = value; count += 1; continue; @@ -514,14 +540,17 @@ pub const Font = struct { } if (stretch == null) { - if (input.tryParse(FontStretchKeyword.parse, .{})) |value| { - stretch = value; + if (input.tryParse(FontStretchKeyword.parse, .{}).asValue()) |value| { + stretch = .{ .keyword = value }; count += 1; continue; } } - size = try FontSize.parse(input); + size = switch (@call(.auto, @field(FontSize, "parse"), .{input})) { + .result => |v| v, + .err => |e| return .{ .err = e }, + }; break; } @@ -529,11 +558,17 @@ pub const Font = struct { const final_size = size orelse return .{ .err = input.newCustomError(css.ParserError.invalid_declaration) }; - const line_height = if (input.tryParse(css.Parser.expectDelim, .{'/'}).isOk()) try LineHeight.parse(input) else null; + const line_height = if (input.tryParse(css.Parser.expectDelim, .{'/'}).isOk()) switch (LineHeight.parse(input)) { + .result => |v| v, + .err => |e| return .{ .err = e }, + } else null; - const family = input.parseCommaSeparated(FontFamily, FontFamily.parse); + const family = switch (bun.BabyList(FontFamily).parse(input)) { + .result => |v| v, + .err => |e| return .{ .err = e }, + }; - return Font{ + return .{ .result = Font{ .family = family, .size = final_size, .style = style orelse FontStyle.default(), @@ -541,47 +576,55 @@ pub const Font = struct { .stretch = stretch orelse FontStretch.default(), .line_height = line_height orelse LineHeight.default(), .variant_caps = variant_caps orelse FontVariantCaps.default(), - }; + } }; } pub fn toCss(this: *const Font, comptime W: type, dest: *Printer(W)) PrintErr!void { - if (this.style != FontStyle.default()) { + if (!this.style.eql(&FontStyle.default())) { try this.style.toCss(W, dest); try dest.writeChar(' '); } - if (this.variant_caps != FontVariantCaps.default()) { + if (!this.variant_caps.eql(&FontVariantCaps.default())) { try this.variant_caps.toCss(W, dest); try dest.writeChar(' '); } - if (this.weight != FontWeight.default()) { + if (!this.weight.eql(&FontWeight.default())) { try this.weight.toCss(W, dest); try dest.writeChar(' '); } - if (this.stretch != FontStretch.default()) { + if (!this.stretch.eql(&FontStretch.default())) { try this.stretch.toCss(W, dest); try dest.writeChar(' '); } try this.size.toCss(W, dest); - if (this.line_height != LineHeight.default()) { + if (!this.line_height.eql(&LineHeight.default())) { try dest.delim('/', true); try this.line_height.toCss(W, dest); } try dest.writeChar(' '); - const len = this.family.items.len; - for (this.family.items, 0..) |*val, idx| { + const len = this.family.len; + for (this.family.sliceConst(), 0..) |*val, idx| { try val.toCss(W, dest); if (idx < len - 1) { try dest.delim(',', false); } } } + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } }; /// A value for the [vertical align](https://drafts.csswg.org/css2/#propdef-vertical-align) property. diff --git a/src/css/properties/generate_properties.ts b/src/css/properties/generate_properties.ts index d68e9ca701..71cb9c2961 100644 --- a/src/css/properties/generate_properties.ts +++ b/src/css/properties/generate_properties.ts @@ -29,11 +29,17 @@ type PropertyDef = { conditional?: { css_modules: boolean; }; + eval_branch_quota?: number; }; const OUTPUT_FILE = "src/css/properties/properties_generated.zig"; async function generateCode(property_defs: Record) { + const EMIT_COMPLETED_MD_FILE = true; + if (EMIT_COMPLETED_MD_FILE) { + const completed = Object.entries(property_defs).map(([name, meta]) => `- [x] \`${name}\``).join("\n"); + await Bun.$`echo ${completed} > completed.md` + } await Bun.$`echo ${prelude()} > ${OUTPUT_FILE}`; await Bun.$`echo ${generateProperty(property_defs)} >> ${OUTPUT_FILE}`; await Bun.$`echo ${generatePropertyId(property_defs)} >> ${OUTPUT_FILE}`; @@ -66,8 +72,47 @@ ${Object.entries(property_defs) } function generatePropertyImpl(property_defs: Record): string { + const required_functions = [ + "deepClone", + "parse", + "toCss", + "eql", + ]; + return ` pub usingnamespace PropertyImpl(); + + // Sanity check to make sure all types have the following functions: + // - deepClone() + // - eql() + // - parse() + // - toCss() + // + // We do this string concatenation thing so we get all the errors at once, + // instead of relying on Zig semantic analysis which usualy stops at the first error. + comptime { + const compile_error: []const u8 = compile_error: { + var compile_error: []const u8 = ""; + ${Object.entries(property_defs) + .map(([name, meta]) => { + if (meta.ty != "void" && meta.ty != "CSSNumber" && meta.ty != "CSSInteger") { + return required_functions.map(fn => ` + if (!@hasDecl(${meta.ty}, "${fn}")) { + compile_error = compile_error ++ @typeName(${meta.ty}) ++ ": does not have a ${fn}() function.\\n"; + } + `).join("\n"); + } + return ""; + }) + .join("\n")} + const final_compile_error = compile_error; + break :compile_error final_compile_error; + }; + if (compile_error.len > 0) { + @compileError(compile_error); + } + } + /// Parses a CSS property by name. pub fn parse(property_id: PropertyId, input: *css.Parser, options: *const css.ParserOptions) Result(Property) { const state = input.state(); @@ -96,6 +141,50 @@ function generatePropertyImpl(property_defs: Record): strin } } }; } + pub fn propertyId(this: *const Property) PropertyId { + return switch (this.*) { + ${Object.entries(property_defs) + .map(([name, meta]) => { + if (meta.valid_prefixes !== undefined) { + return `.${escapeIdent(name)} => |*v| PropertyId{ .${escapeIdent(name)} = v[1] },`; + } + return `.${escapeIdent(name)} => .${escapeIdent(name)},`; + }) + .join("\n")} + .all => PropertyId.all, + .unparsed => |unparsed| unparsed.property_id, + .custom => |c| .{ .custom = c.name }, + }; + } + + pub fn deepClone(this: *const Property, allocator: std.mem.Allocator) Property { + return switch (this.*) { + ${Object.entries(property_defs) + .map(([name, meta]) => { + if (meta.valid_prefixes !== undefined) { + const clone_expr = (meta.ty === "CSSNumber" || meta.ty === "CSSInteger") ? "v[0]" : "v[0].deepClone(allocator)"; + return `.${escapeIdent(name)} => |*v| .{ .${escapeIdent(name)} = .{ ${clone_expr}, v[1] } },`; + } + const clone_expr = (meta.ty === "CSSNumber" || meta.ty === "CSSInteger") ? "v.*" : meta.ty.includes("BabyList(") ? `css.generic.deepClone(${meta.ty}, v, allocator)` : "v.deepClone(allocator)"; + return `.${escapeIdent(name)} => |*v| .{ .${escapeIdent(name)} = ${clone_expr} },`; + }) + .join("\n")} + .all => |*a| return .{ .all = a.deepClone(allocator) }, + .unparsed => |*u| return .{ .unparsed = u.deepClone(allocator) }, + .custom => |*c| return .{ .custom = c.deepClone(allocator) }, + }; + } + + /// We're going to have this empty for now since not every property has a deinit function. + /// It's not strictly necessary since all allocations are into an arena. + /// It's mostly intended as a performance optimization in the case where mimalloc arena is used, + /// since it can reclaim the memory and use it for subsequent allocations. + /// I haven't benchmarked that though, so I don't actually know how much faster it would actually make it. + pub fn deinit(this: *@This(), allocator: std.mem.Allocator) void { + _ = this; + _ = allocator; + } + pub inline fn __toCssHelper(this: *const Property) struct{[]const u8, VendorPrefix} { return switch (this.*) { ${generatePropertyImplToCssHelper(property_defs)} @@ -117,7 +206,8 @@ function generatePropertyImpl(property_defs: Record): strin ${Object.entries(property_defs) .map(([name, meta]) => { const value = meta.valid_prefixes === undefined ? "value" : "value[0]"; - return `.${escapeIdent(name)} => |*value| ${value}.toCss(W, dest),`; + const to_css = meta.ty === "CSSNumber" ? `CSSNumberFns.toCss(&${value}, W, dest)` : meta.ty === "CSSInteger" ? `CSSIntegerFns.toCss(&${value}, W, dest)` : meta.ty.includes("ArrayList") ? `css.generic.toCss(${meta.ty}, ${value}, W, dest)` : `${value}.toCss(W, dest)`; + return `.${escapeIdent(name)} => |*value| ${to_css},`; }) .join("\n")} .all => |*keyword| keyword.toCss(W, dest), @@ -146,6 +236,21 @@ function generatePropertyImpl(property_defs: Record): strin } return null; } + + pub fn eql(lhs: *const Property, rhs: *const Property) bool { + if (@intFromEnum(lhs.*) != @intFromEnum(rhs.*)) return false; + return switch (lhs.*) { + ${Object.entries(property_defs) + .map(([name, meta]) => { + + if (meta.valid_prefixes !== undefined) return `.${escapeIdent(name)} => |*v| css.generic.eql(${meta.ty}, &v[0], &v[0]) and v[1].eq(rhs.${escapeIdent(name)}[1]),`; + return `.${escapeIdent(name)} => |*v| css.generic.eql(${meta.ty}, v, &rhs.${escapeIdent(name)}),`; + }) + .join("\n")} + .all, .unparsed => true, + .custom => |*c| c.eql(&rhs.custom), + }; + } `; } @@ -168,6 +273,7 @@ function generatePropertyImplParseCases(property_defs: Record ${capture} { + ${meta.eval_branch_quota !== undefined ? `@setEvalBranchQuota(${meta.eval_branch_quota});` : ""} if (css.generic.parseWithOptions(${meta.ty}, input, options).asValue()) |c| { if (input.expectExhausted().isOk()) { return .{ .result = ${ret} }; @@ -233,7 +339,6 @@ function generatePropertyIdImpl(property_defs: Record): str return null; } - pub fn withPrefix(this: *const PropertyId, pre: VendorPrefix) PropertyId { return switch (this.*) { ${Object.entries(property_defs) @@ -257,6 +362,29 @@ function generatePropertyIdImpl(property_defs: Record): str else => {}, }; } + + pub inline fn deepClone(this: *const PropertyId, _: std.mem.Allocator) PropertyId { + return this.*; + } + + pub fn eql(lhs: *const PropertyId, rhs: *const PropertyId) bool { + if (@intFromEnum(lhs.*) != @intFromEnum(rhs.*)) return false; + inline for (bun.meta.EnumFields(PropertyId), std.meta.fields(PropertyId)) |enum_field, union_field| { + if (enum_field.value == @intFromEnum(lhs.*)) { + if (comptime union_field.type == css.VendorPrefix) { + return @field(lhs, union_field.name).eql(@field(rhs, union_field.name)); + } else { + return true; + } + } + } + unreachable; + } + + pub fn hash(this: *const PropertyId, hasher: *std.hash.Wyhash) void { + const tag = @intFromEnum(this.*); + hasher.update(std.mem.asBytes(&tag)); + } `; } @@ -309,170 +437,170 @@ generateCode({ "background-color": { ty: "CssColor", }, - // "background-image": { - // ty: "SmallList(Image, 1)", - // }, - // "background-position-x": { - // ty: "SmallList(css_values.position.HorizontalPosition, 1)", - // }, - // "background-position-y": { - // ty: "SmallList(css_values.position.HorizontalPosition, 1)", - // }, - // "background-position": { - // ty: "SmallList(background.BackgroundPosition, 1)", - // shorthand: true, - // }, - // "background-size": { - // ty: "SmallList(background.BackgroundSize, 1)", - // }, - // "background-repeat": { - // ty: "SmallList(background.BackgroundSize, 1)", - // }, - // "background-attachment": { - // ty: "SmallList(background.BackgroundAttachment, 1)", - // }, - // "background-clip": { - // ty: "SmallList(background.BackgroundAttachment, 1)", - // valid_prefixes: ["webkit", "moz"], - // }, - // "background-origin": { - // ty: "SmallList(background.BackgroundOrigin, 1)", - // }, - // background: { - // ty: "SmallList(background.Background, 1)", - // }, - // "box-shadow": { - // ty: "SmallList(box_shadow.BoxShadow, 1)", - // valid_prefixes: ["webkit", "moz"], - // }, - // opacity: { - // ty: "css.css_values.alpha.AlphaValue", - // }, + "background-image": { + ty: "SmallList(Image, 1)", + }, + "background-position-x": { + ty: "SmallList(css_values.position.HorizontalPosition, 1)", + }, + "background-position-y": { + ty: "SmallList(css_values.position.HorizontalPosition, 1)", + }, + "background-position": { + ty: "SmallList(background.BackgroundPosition, 1)", + shorthand: true, + }, + "background-size": { + ty: "SmallList(background.BackgroundSize, 1)", + }, + "background-repeat": { + ty: "SmallList(background.BackgroundSize, 1)", + }, + "background-attachment": { + ty: "SmallList(background.BackgroundAttachment, 1)", + }, + "background-clip": { + ty: "SmallList(background.BackgroundAttachment, 1)", + valid_prefixes: ["webkit", "moz"], + }, + "background-origin": { + ty: "SmallList(background.BackgroundOrigin, 1)", + }, + background: { + ty: "SmallList(background.Background, 1)", + }, + "box-shadow": { + ty: "SmallList(box_shadow.BoxShadow, 1)", + valid_prefixes: ["webkit", "moz"], + }, + opacity: { + ty: "css.css_values.alpha.AlphaValue", + }, color: { ty: "CssColor", }, - // display: { - // ty: "display.Display", - // }, - // visibility: { - // ty: "display.Visibility", - // }, - // width: { - // ty: "size.Size", - // logical_group: { ty: "size", category: "physical" }, - // }, - // height: { - // ty: "size.Size", - // logical_group: { ty: "size", category: "physical" }, - // }, - // "min-width": { - // ty: "size.Size", - // logical_group: { ty: "min_size", category: "physical" }, - // }, - // "min-height": { - // ty: "size.Size", - // logical_group: { ty: "min_size", category: "physical" }, - // }, - // "max-width": { - // ty: "size.MaxSize", - // logical_group: { ty: "max_size", category: "physical" }, - // }, - // "max-height": { - // ty: "size.MaxSize", - // logical_group: { ty: "max_size", category: "physical" }, - // }, - // "block-size": { - // ty: "size.Size", - // logical_group: { ty: "size", category: "logical" }, - // }, - // "inline-size": { - // ty: "size.Size", - // logical_group: { ty: "size", category: "logical" }, - // }, - // "min-block-size": { - // ty: "size.Size", - // logical_group: { ty: "min_size", category: "logical" }, - // }, - // "min-inline-size": { - // ty: "size.Size", - // logical_group: { ty: "min_size", category: "logical" }, - // }, - // "max-block-size": { - // ty: "size.MaxSize", - // logical_group: { ty: "max_size", category: "logical" }, - // }, - // "max-inline-size": { - // ty: "size.MaxSize", - // logical_group: { ty: "max_size", category: "logical" }, - // }, - // "box-sizing": { - // ty: "size.BoxSizing", - // valid_prefixes: ["webkit", "moz"], - // }, - // "aspect-ratio": { - // ty: "size.AspectRatio", - // }, - // overflow: { - // ty: "overflow.Overflow", - // shorthand: true, - // }, - // "overflow-x": { - // ty: "overflow.OverflowKeyword", - // }, - // "overflow-y": { - // ty: "overflow.OverflowKeyword", - // }, - // "text-overflow": { - // ty: "overflow.TextOverflow", - // valid_prefixes: ["o"], - // }, - // position: { - // ty: "position.Position", - // }, - // top: { - // ty: "LengthPercentageOrAuto", - // logical_group: { ty: "inset", category: "physical" }, - // }, - // bottom: { - // ty: "LengthPercentageOrAuto", - // logical_group: { ty: "inset", category: "physical" }, - // }, - // left: { - // ty: "LengthPercentageOrAuto", - // logical_group: { ty: "inset", category: "physical" }, - // }, - // right: { - // ty: "LengthPercentageOrAuto", - // logical_group: { ty: "inset", category: "physical" }, - // }, - // "inset-block-start": { - // ty: "LengthPercentageOrAuto", - // logical_group: { ty: "inset", category: "logical" }, - // }, - // "inset-block-end": { - // ty: "LengthPercentageOrAuto", - // logical_group: { ty: "inset", category: "logical" }, - // }, - // "inset-inline-start": { - // ty: "LengthPercentageOrAuto", - // logical_group: { ty: "inset", category: "logical" }, - // }, - // "inset-inline-end": { - // ty: "LengthPercentageOrAuto", - // logical_group: { ty: "inset", category: "logical" }, - // }, - // "inset-block": { - // ty: "margin_padding.InsetBlock", - // shorthand: true, - // }, - // "inset-inline": { - // ty: "margin_padding.InsetInline", - // shorthand: true, - // }, - // inset: { - // ty: "margin_padding.Inset", - // shorthand: true, - // }, + display: { + ty: "display.Display", + }, + visibility: { + ty: "display.Visibility", + }, + width: { + ty: "size.Size", + logical_group: { ty: "size", category: "physical" }, + }, + height: { + ty: "size.Size", + logical_group: { ty: "size", category: "physical" }, + }, + "min-width": { + ty: "size.Size", + logical_group: { ty: "min_size", category: "physical" }, + }, + "min-height": { + ty: "size.Size", + logical_group: { ty: "min_size", category: "physical" }, + }, + "max-width": { + ty: "size.MaxSize", + logical_group: { ty: "max_size", category: "physical" }, + }, + "max-height": { + ty: "size.MaxSize", + logical_group: { ty: "max_size", category: "physical" }, + }, + "block-size": { + ty: "size.Size", + logical_group: { ty: "size", category: "logical" }, + }, + "inline-size": { + ty: "size.Size", + logical_group: { ty: "size", category: "logical" }, + }, + "min-block-size": { + ty: "size.Size", + logical_group: { ty: "min_size", category: "logical" }, + }, + "min-inline-size": { + ty: "size.Size", + logical_group: { ty: "min_size", category: "logical" }, + }, + "max-block-size": { + ty: "size.MaxSize", + logical_group: { ty: "max_size", category: "logical" }, + }, + "max-inline-size": { + ty: "size.MaxSize", + logical_group: { ty: "max_size", category: "logical" }, + }, + "box-sizing": { + ty: "size.BoxSizing", + valid_prefixes: ["webkit", "moz"], + }, + "aspect-ratio": { + ty: "size.AspectRatio", + }, + overflow: { + ty: "overflow.Overflow", + shorthand: true, + }, + "overflow-x": { + ty: "overflow.OverflowKeyword", + }, + "overflow-y": { + ty: "overflow.OverflowKeyword", + }, + "text-overflow": { + ty: "overflow.TextOverflow", + valid_prefixes: ["o"], + }, + position: { + ty: "position.Position", + }, + top: { + ty: "LengthPercentageOrAuto", + logical_group: { ty: "inset", category: "physical" }, + }, + bottom: { + ty: "LengthPercentageOrAuto", + logical_group: { ty: "inset", category: "physical" }, + }, + left: { + ty: "LengthPercentageOrAuto", + logical_group: { ty: "inset", category: "physical" }, + }, + right: { + ty: "LengthPercentageOrAuto", + logical_group: { ty: "inset", category: "physical" }, + }, + "inset-block-start": { + ty: "LengthPercentageOrAuto", + logical_group: { ty: "inset", category: "logical" }, + }, + "inset-block-end": { + ty: "LengthPercentageOrAuto", + logical_group: { ty: "inset", category: "logical" }, + }, + "inset-inline-start": { + ty: "LengthPercentageOrAuto", + logical_group: { ty: "inset", category: "logical" }, + }, + "inset-inline-end": { + ty: "LengthPercentageOrAuto", + logical_group: { ty: "inset", category: "logical" }, + }, + "inset-block": { + ty: "margin_padding.InsetBlock", + shorthand: true, + }, + "inset-inline": { + ty: "margin_padding.InsetInline", + shorthand: true, + }, + inset: { + ty: "margin_padding.Inset", + shorthand: true, + }, "border-spacing": { ty: "css.css_values.size.Size2D(Length)", }, @@ -532,14 +660,14 @@ generateCode({ ty: "border.LineStyle", logical_group: { ty: "border_style", category: "logical" }, }, - // "border-inline-start-style": { - // ty: "border.LineStyle", - // logical_group: { ty: "border_style", category: "logical" }, - // }, - // "border-inline-end-style": { - // ty: "border.LineStyle", - // logical_group: { ty: "border_style", category: "logical" }, - // }, + "border-inline-start-style": { + ty: "border.LineStyle", + logical_group: { ty: "border_style", category: "logical" }, + }, + "border-inline-end-style": { + ty: "border.LineStyle", + logical_group: { ty: "border_style", category: "logical" }, + }, "border-top-width": { ty: "BorderSideWidth", logical_group: { ty: "border_width", category: "physical" }, @@ -556,535 +684,536 @@ generateCode({ ty: "BorderSideWidth", logical_group: { ty: "border_width", category: "physical" }, }, - // "border-block-start-width": { - // ty: "BorderSideWidth", - // logical_group: { ty: "border_width", category: "logical" }, - // }, - // "border-block-end-width": { - // ty: "BorderSideWidth", - // logical_group: { ty: "border_width", category: "logical" }, - // }, - // "border-inline-start-width": { - // ty: "BorderSideWidth", - // logical_group: { ty: "border_width", category: "logical" }, - // }, - // "border-inline-end-width": { - // ty: "BorderSideWidth", - // logical_group: { ty: "border_width", category: "logical" }, - // }, - // "border-top-left-radius": { - // ty: "Size2D(LengthPercentage)", - // valid_prefixes: ["webkit", "moz"], - // logical_group: { ty: "border_radius", category: "physical" }, - // }, - // "border-top-right-radius": { - // ty: "Size2D(LengthPercentage)", - // valid_prefixes: ["webkit", "moz"], - // logical_group: { ty: "border_radius", category: "physical" }, - // }, - // "border-bottom-left-radius": { - // ty: "Size2D(LengthPercentage)", - // valid_prefixes: ["webkit", "moz"], - // logical_group: { ty: "border_radius", category: "physical" }, - // }, - // "border-bottom-right-radius": { - // ty: "Size2D(LengthPercentage)", - // valid_prefixes: ["webkit", "moz"], - // logical_group: { ty: "border_radius", category: "physical" }, - // }, - // "border-start-start-radius": { - // ty: "Size2D(LengthPercentage)", - // logical_group: { ty: "border_radius", category: "logical" }, - // }, - // "border-start-end-radius": { - // ty: "Size2D(LengthPercentage)", - // logical_group: { ty: "border_radius", category: "logical" }, - // }, - // "border-end-start-radius": { - // ty: "Size2D(LengthPercentage)", - // logical_group: { ty: "border_radius", category: "logical" }, - // }, - // "border-end-end-radius": { - // ty: "Size2D(LengthPercentage)", - // logical_group: { ty: "border_radius", category: "logical" }, - // }, - // "border-radius": { - // ty: "BorderRadius", - // valid_prefixes: ["webkit", "moz"], - // shorthand: true, - // }, - // "border-image-source": { - // ty: "Image", - // }, - // "border-image-outset": { - // ty: "Rect(LengthOrNumber)", - // }, - // "border-image-repeat": { - // ty: "BorderImageRepeat", - // }, - // "border-image-width": { - // ty: "Rect(BorderImageSideWidth)", - // }, - // "border-image-slice": { - // ty: "BorderImageSlice", - // }, - // "border-image": { - // ty: "BorderImage", - // valid_prefixes: ["webkit", "moz", "o"], - // shorthand: true, - // }, - // "border-color": { - // ty: "BorderColor", - // shorthand: true, - // }, - // "border-style": { - // ty: "BorderStyle", - // shorthand: true, - // }, - // "border-width": { - // ty: "BorderWidth", - // shorthand: true, - // }, - // "border-block-color": { - // ty: "BorderBlockColor", - // shorthand: true, - // }, - // "border-block-style": { - // ty: "BorderBlockStyle", - // shorthand: true, - // }, - // "border-block-width": { - // ty: "BorderBlockWidth", - // shorthand: true, - // }, - // "border-inline-color": { - // ty: "BorderInlineColor", - // shorthand: true, - // }, - // "border-inline-style": { - // ty: "BorderInlineStyle", - // shorthand: true, - // }, - // "border-inline-width": { - // ty: "BorderInlineWidth", - // shorthand: true, - // }, - // border: { - // ty: "Border", - // shorthand: true, - // }, - // "border-top": { - // ty: "BorderTop", - // shorthand: true, - // }, - // "border-bottom": { - // ty: "BorderBottom", - // shorthand: true, - // }, - // "border-left": { - // ty: "BorderLeft", - // shorthand: true, - // }, - // "border-right": { - // ty: "BorderRight", - // shorthand: true, - // }, - // "border-block": { - // ty: "BorderBlock", - // shorthand: true, - // }, - // "border-block-start": { - // ty: "BorderBlockStart", - // shorthand: true, - // }, - // "border-block-end": { - // ty: "BorderBlockEnd", - // shorthand: true, - // }, - // "border-inline": { - // ty: "BorderInline", - // shorthand: true, - // }, - // "border-inline-start": { - // ty: "BorderInlineStart", - // shorthand: true, - // }, - // "border-inline-end": { - // ty: "BorderInlineEnd", - // shorthand: true, - // }, - // outline: { - // ty: "Outline", - // shorthand: true, - // }, + "border-block-start-width": { + ty: "BorderSideWidth", + logical_group: { ty: "border_width", category: "logical" }, + }, + "border-block-end-width": { + ty: "BorderSideWidth", + logical_group: { ty: "border_width", category: "logical" }, + }, + "border-inline-start-width": { + ty: "BorderSideWidth", + logical_group: { ty: "border_width", category: "logical" }, + }, + "border-inline-end-width": { + ty: "BorderSideWidth", + logical_group: { ty: "border_width", category: "logical" }, + }, + "border-top-left-radius": { + ty: "Size2D(LengthPercentage)", + valid_prefixes: ["webkit", "moz"], + logical_group: { ty: "border_radius", category: "physical" }, + }, + "border-top-right-radius": { + ty: "Size2D(LengthPercentage)", + valid_prefixes: ["webkit", "moz"], + logical_group: { ty: "border_radius", category: "physical" }, + }, + "border-bottom-left-radius": { + ty: "Size2D(LengthPercentage)", + valid_prefixes: ["webkit", "moz"], + logical_group: { ty: "border_radius", category: "physical" }, + }, + "border-bottom-right-radius": { + ty: "Size2D(LengthPercentage)", + valid_prefixes: ["webkit", "moz"], + logical_group: { ty: "border_radius", category: "physical" }, + }, + "border-start-start-radius": { + ty: "Size2D(LengthPercentage)", + logical_group: { ty: "border_radius", category: "logical" }, + }, + "border-start-end-radius": { + ty: "Size2D(LengthPercentage)", + logical_group: { ty: "border_radius", category: "logical" }, + }, + "border-end-start-radius": { + ty: "Size2D(LengthPercentage)", + logical_group: { ty: "border_radius", category: "logical" }, + }, + "border-end-end-radius": { + ty: "Size2D(LengthPercentage)", + logical_group: { ty: "border_radius", category: "logical" }, + }, + "border-radius": { + ty: "BorderRadius", + valid_prefixes: ["webkit", "moz"], + shorthand: true, + }, + "border-image-source": { + ty: "Image", + }, + "border-image-outset": { + ty: "Rect(LengthOrNumber)", + }, + "border-image-repeat": { + ty: "BorderImageRepeat", + }, + "border-image-width": { + ty: "Rect(BorderImageSideWidth)", + }, + "border-image-slice": { + ty: "BorderImageSlice", + }, + "border-image": { + ty: "BorderImage", + valid_prefixes: ["webkit", "moz", "o"], + shorthand: true, + }, + "border-color": { + ty: "BorderColor", + shorthand: true, + }, + "border-style": { + ty: "BorderStyle", + shorthand: true, + }, + "border-width": { + ty: "BorderWidth", + shorthand: true, + }, + "border-block-color": { + ty: "BorderBlockColor", + shorthand: true, + }, + "border-block-style": { + ty: "BorderBlockStyle", + shorthand: true, + }, + "border-block-width": { + ty: "BorderBlockWidth", + shorthand: true, + }, + "border-inline-color": { + ty: "BorderInlineColor", + shorthand: true, + }, + "border-inline-style": { + ty: "BorderInlineStyle", + shorthand: true, + }, + "border-inline-width": { + ty: "BorderInlineWidth", + shorthand: true, + }, + border: { + ty: "Border", + shorthand: true, + }, + "border-top": { + ty: "BorderTop", + shorthand: true, + }, + "border-bottom": { + ty: "BorderBottom", + shorthand: true, + }, + "border-left": { + ty: "BorderLeft", + shorthand: true, + }, + "border-right": { + ty: "BorderRight", + shorthand: true, + }, + "border-block": { + ty: "BorderBlock", + shorthand: true, + }, + "border-block-start": { + ty: "BorderBlockStart", + shorthand: true, + }, + "border-block-end": { + ty: "BorderBlockEnd", + shorthand: true, + }, + "border-inline": { + ty: "BorderInline", + shorthand: true, + }, + "border-inline-start": { + ty: "BorderInlineStart", + shorthand: true, + }, + "border-inline-end": { + ty: "BorderInlineEnd", + shorthand: true, + }, + outline: { + ty: "Outline", + shorthand: true, + }, "outline-color": { ty: "CssColor", }, - // "outline-style": { - // ty: "OutlineStyle", - // }, - // "outline-width": { - // ty: "BorderSideWidth", - // }, - // "flex-direction": { - // ty: "FlexDirection", - // valid_prefixes: ["webkit", "ms"], - // }, - // "flex-wrap": { - // ty: "FlexWrap", - // valid_prefixes: ["webkit", "ms"], - // }, - // "flex-flow": { - // ty: "FlexFlow", - // valid_prefixes: ["webkit", "ms"], - // shorthand: true, - // }, - // "flex-grow": { - // ty: "CSSNumber", - // valid_prefixes: ["webkit"], - // }, - // "flex-shrink": { - // ty: "CSSNumber", - // valid_prefixes: ["webkit"], - // }, - // "flex-basis": { - // ty: "LengthPercentageOrAuto", - // valid_prefixes: ["webkit"], - // }, - // flex: { - // ty: "Flex", - // valid_prefixes: ["webkit", "ms"], - // shorthand: true, - // }, - // order: { - // ty: "CSSInteger", - // valid_prefixes: ["webkit"], - // }, - // "align-content": { - // ty: "AlignContent", - // valid_prefixes: ["webkit"], - // }, - // "justify-content": { - // ty: "JustifyContent", - // valid_prefixes: ["webkit"], - // }, - // "place-content": { - // ty: "PlaceContent", - // shorthand: true, - // }, - // "align-self": { - // ty: "AlignSelf", - // valid_prefixes: ["webkit"], - // }, - // "justify-self": { - // ty: "JustifySelf", - // }, - // "place-self": { - // ty: "PlaceSelf", - // shorthand: true, - // }, - // "align-items": { - // ty: "AlignItems", - // valid_prefixes: ["webkit"], - // }, - // "justify-items": { - // ty: "JustifyItems", - // }, - // "place-items": { - // ty: "PlaceItems", - // shorthand: true, - // }, - // "row-gap": { - // ty: "GapValue", - // }, - // "column-gap": { - // ty: "GapValue", - // }, - // gap: { - // ty: "Gap", - // shorthand: true, - // }, - // "box-orient": { - // ty: "BoxOrient", - // valid_prefixes: ["webkit", "moz"], - // unprefixed: false, - // }, - // "box-direction": { - // ty: "BoxDirection", - // valid_prefixes: ["webkit", "moz"], - // unprefixed: false, - // }, - // "box-ordinal-group": { - // ty: "CSSInteger", - // valid_prefixes: ["webkit", "moz"], - // unprefixed: false, - // }, - // "box-align": { - // ty: "BoxAlign", - // valid_prefixes: ["webkit", "moz"], - // unprefixed: false, - // }, - // "box-flex": { - // ty: "CSSNumber", - // valid_prefixes: ["webkit", "moz"], - // unprefixed: false, - // }, - // "box-flex-group": { - // ty: "CSSInteger", - // valid_prefixes: ["webkit"], - // unprefixed: false, - // }, - // "box-pack": { - // ty: "BoxPack", - // valid_prefixes: ["webkit", "moz"], - // unprefixed: false, - // }, - // "box-lines": { - // ty: "BoxLines", - // valid_prefixes: ["webkit", "moz"], - // unprefixed: false, - // }, - // "flex-pack": { - // ty: "FlexPack", - // valid_prefixes: ["ms"], - // unprefixed: false, - // }, - // "flex-order": { - // ty: "CSSInteger", - // valid_prefixes: ["ms"], - // unprefixed: false, - // }, - // "flex-align": { - // ty: "BoxAlign", - // valid_prefixes: ["ms"], - // unprefixed: false, - // }, - // "flex-item-align": { - // ty: "FlexItemAlign", - // valid_prefixes: ["ms"], - // unprefixed: false, - // }, - // "flex-line-pack": { - // ty: "FlexLinePack", - // valid_prefixes: ["ms"], - // unprefixed: false, - // }, - // "flex-positive": { - // ty: "CSSNumber", - // valid_prefixes: ["ms"], - // unprefixed: false, - // }, - // "flex-negative": { - // ty: "CSSNumber", - // valid_prefixes: ["ms"], - // unprefixed: false, - // }, - // "flex-preferred-size": { - // ty: "LengthPercentageOrAuto", - // valid_prefixes: ["ms"], - // unprefixed: false, - // }, - // "margin-top": { - // ty: "LengthPercentageOrAuto", - // logical_group: { ty: "margin", category: "physical" }, - // }, - // "margin-bottom": { - // ty: "LengthPercentageOrAuto", - // logical_group: { ty: "margin", category: "physical" }, - // }, - // "margin-left": { - // ty: "LengthPercentageOrAuto", - // logical_group: { ty: "margin", category: "physical" }, - // }, - // "margin-right": { - // ty: "LengthPercentageOrAuto", - // logical_group: { ty: "margin", category: "physical" }, - // }, - // "margin-block-start": { - // ty: "LengthPercentageOrAuto", - // logical_group: { ty: "margin", category: "logical" }, - // }, - // "margin-block-end": { - // ty: "LengthPercentageOrAuto", - // logical_group: { ty: "margin", category: "logical" }, - // }, - // "margin-inline-start": { - // ty: "LengthPercentageOrAuto", - // logical_group: { ty: "margin", category: "logical" }, - // }, - // "margin-inline-end": { - // ty: "LengthPercentageOrAuto", - // logical_group: { ty: "margin", category: "logical" }, - // }, - // "margin-block": { - // ty: "MarginBlock", - // shorthand: true, - // }, - // "margin-inline": { - // ty: "MarginInline", - // shorthand: true, - // }, - // margin: { - // ty: "Margin", - // shorthand: true, - // }, - // "padding-top": { - // ty: "LengthPercentageOrAuto", - // logical_group: { ty: "padding", category: "physical" }, - // }, - // "padding-bottom": { - // ty: "LengthPercentageOrAuto", - // logical_group: { ty: "padding", category: "physical" }, - // }, - // "padding-left": { - // ty: "LengthPercentageOrAuto", - // logical_group: { ty: "padding", category: "physical" }, - // }, - // "padding-right": { - // ty: "LengthPercentageOrAuto", - // logical_group: { ty: "padding", category: "physical" }, - // }, - // "padding-block-start": { - // ty: "LengthPercentageOrAuto", - // logical_group: { ty: "padding", category: "logical" }, - // }, - // "padding-block-end": { - // ty: "LengthPercentageOrAuto", - // logical_group: { ty: "padding", category: "logical" }, - // }, - // "padding-inline-start": { - // ty: "LengthPercentageOrAuto", - // logical_group: { ty: "padding", category: "logical" }, - // }, - // "padding-inline-end": { - // ty: "LengthPercentageOrAuto", - // logical_group: { ty: "padding", category: "logical" }, - // }, - // "padding-block": { - // ty: "PaddingBlock", - // shorthand: true, - // }, - // "padding-inline": { - // ty: "PaddingInline", - // shorthand: true, - // }, - // padding: { - // ty: "Padding", - // shorthand: true, - // }, - // "scroll-margin-top": { - // ty: "LengthPercentageOrAuto", - // logical_group: { ty: "scroll_margin", category: "physical" }, - // }, - // "scroll-margin-bottom": { - // ty: "LengthPercentageOrAuto", - // logical_group: { ty: "scroll_margin", category: "physical" }, - // }, - // "scroll-margin-left": { - // ty: "LengthPercentageOrAuto", - // logical_group: { ty: "scroll_margin", category: "physical" }, - // }, - // "scroll-margin-right": { - // ty: "LengthPercentageOrAuto", - // logical_group: { ty: "scroll_margin", category: "physical" }, - // }, - // "scroll-margin-block-start": { - // ty: "LengthPercentageOrAuto", - // logical_group: { ty: "scroll_margin", category: "logical" }, - // }, - // "scroll-margin-block-end": { - // ty: "LengthPercentageOrAuto", - // logical_group: { ty: "scroll_margin", category: "logical" }, - // }, - // "scroll-margin-inline-start": { - // ty: "LengthPercentageOrAuto", - // logical_group: { ty: "scroll_margin", category: "logical" }, - // }, - // "scroll-margin-inline-end": { - // ty: "LengthPercentageOrAuto", - // logical_group: { ty: "scroll_margin", category: "logical" }, - // }, - // "scroll-margin-block": { - // ty: "ScrollMarginBlock", - // shorthand: true, - // }, - // "scroll-margin-inline": { - // ty: "ScrollMarginInline", - // shorthand: true, - // }, - // "scroll-margin": { - // ty: "ScrollMargin", - // shorthand: true, - // }, - // "scroll-padding-top": { - // ty: "LengthPercentageOrAuto", - // logical_group: { ty: "scroll_padding", category: "physical" }, - // }, - // "scroll-padding-bottom": { - // ty: "LengthPercentageOrAuto", - // logical_group: { ty: "scroll_padding", category: "physical" }, - // }, - // "scroll-padding-left": { - // ty: "LengthPercentageOrAuto", - // logical_group: { ty: "scroll_padding", category: "physical" }, - // }, - // "scroll-padding-right": { - // ty: "LengthPercentageOrAuto", - // logical_group: { ty: "scroll_padding", category: "physical" }, - // }, - // "scroll-padding-block-start": { - // ty: "LengthPercentageOrAuto", - // logical_group: { ty: "scroll_padding", category: "logical" }, - // }, - // "scroll-padding-block-end": { - // ty: "LengthPercentageOrAuto", - // logical_group: { ty: "scroll_padding", category: "logical" }, - // }, - // "scroll-padding-inline-start": { - // ty: "LengthPercentageOrAuto", - // logical_group: { ty: "scroll_padding", category: "logical" }, - // }, - // "scroll-padding-inline-end": { - // ty: "LengthPercentageOrAuto", - // logical_group: { ty: "scroll_padding", category: "logical" }, - // }, - // "scroll-padding-block": { - // ty: "ScrollPaddingBlock", - // shorthand: true, - // }, - // "scroll-padding-inline": { - // ty: "ScrollPaddingInline", - // shorthand: true, - // }, - // "scroll-padding": { - // ty: "ScrollPadding", - // shorthand: true, - // }, - // "font-weight": { - // ty: "FontWeight", - // }, - // "font-size": { - // ty: "FontSize", - // }, - // "font-stretch": { - // ty: "FontStretch", - // }, - // "font-family": { - // ty: "ArrayList(FontFamily)", - // }, - // "font-style": { - // ty: "FontStyle", - // }, - // "font-variant-caps": { - // ty: "FontVariantCaps", - // }, - // "line-height": { - // ty: "LineHeight", - // }, - // font: { - // ty: "Font", - // shorthand: true, - // }, + "outline-style": { + ty: "OutlineStyle", + }, + "outline-width": { + ty: "BorderSideWidth", + }, + "flex-direction": { + ty: "FlexDirection", + valid_prefixes: ["webkit", "ms"], + }, + "flex-wrap": { + ty: "FlexWrap", + valid_prefixes: ["webkit", "ms"], + }, + "flex-flow": { + ty: "FlexFlow", + valid_prefixes: ["webkit", "ms"], + shorthand: true, + }, + "flex-grow": { + ty: "CSSNumber", + valid_prefixes: ["webkit"], + }, + "flex-shrink": { + ty: "CSSNumber", + valid_prefixes: ["webkit"], + }, + "flex-basis": { + ty: "LengthPercentageOrAuto", + valid_prefixes: ["webkit"], + }, + flex: { + ty: "Flex", + valid_prefixes: ["webkit", "ms"], + shorthand: true, + }, + order: { + ty: "CSSInteger", + valid_prefixes: ["webkit"], + }, + "align-content": { + ty: "AlignContent", + valid_prefixes: ["webkit"], + }, + "justify-content": { + ty: "JustifyContent", + valid_prefixes: ["webkit"], + }, + "place-content": { + ty: "PlaceContent", + shorthand: true, + }, + "align-self": { + ty: "AlignSelf", + valid_prefixes: ["webkit"], + }, + "justify-self": { + ty: "JustifySelf", + }, + "place-self": { + ty: "PlaceSelf", + shorthand: true, + }, + "align-items": { + ty: "AlignItems", + valid_prefixes: ["webkit"], + }, + "justify-items": { + ty: "JustifyItems", + }, + "place-items": { + ty: "PlaceItems", + shorthand: true, + }, + "row-gap": { + ty: "GapValue", + }, + "column-gap": { + ty: "GapValue", + }, + gap: { + ty: "Gap", + shorthand: true, + }, + "box-orient": { + ty: "BoxOrient", + valid_prefixes: ["webkit", "moz"], + unprefixed: false, + }, + "box-direction": { + ty: "BoxDirection", + valid_prefixes: ["webkit", "moz"], + unprefixed: false, + }, + "box-ordinal-group": { + ty: "CSSInteger", + valid_prefixes: ["webkit", "moz"], + unprefixed: false, + }, + "box-align": { + ty: "BoxAlign", + valid_prefixes: ["webkit", "moz"], + unprefixed: false, + }, + "box-flex": { + ty: "CSSNumber", + valid_prefixes: ["webkit", "moz"], + unprefixed: false, + }, + "box-flex-group": { + ty: "CSSInteger", + valid_prefixes: ["webkit"], + unprefixed: false, + }, + "box-pack": { + ty: "BoxPack", + valid_prefixes: ["webkit", "moz"], + unprefixed: false, + }, + "box-lines": { + ty: "BoxLines", + valid_prefixes: ["webkit", "moz"], + unprefixed: false, + }, + "flex-pack": { + ty: "FlexPack", + valid_prefixes: ["ms"], + unprefixed: false, + }, + "flex-order": { + ty: "CSSInteger", + valid_prefixes: ["ms"], + unprefixed: false, + }, + "flex-align": { + ty: "BoxAlign", + valid_prefixes: ["ms"], + unprefixed: false, + }, + "flex-item-align": { + ty: "FlexItemAlign", + valid_prefixes: ["ms"], + unprefixed: false, + }, + "flex-line-pack": { + ty: "FlexLinePack", + valid_prefixes: ["ms"], + unprefixed: false, + }, + "flex-positive": { + ty: "CSSNumber", + valid_prefixes: ["ms"], + unprefixed: false, + }, + "flex-negative": { + ty: "CSSNumber", + valid_prefixes: ["ms"], + unprefixed: false, + }, + "flex-preferred-size": { + ty: "LengthPercentageOrAuto", + valid_prefixes: ["ms"], + unprefixed: false, + }, + "margin-top": { + ty: "LengthPercentageOrAuto", + logical_group: { ty: "margin", category: "physical" }, + }, + "margin-bottom": { + ty: "LengthPercentageOrAuto", + logical_group: { ty: "margin", category: "physical" }, + }, + "margin-left": { + ty: "LengthPercentageOrAuto", + logical_group: { ty: "margin", category: "physical" }, + }, + "margin-right": { + ty: "LengthPercentageOrAuto", + logical_group: { ty: "margin", category: "physical" }, + }, + "margin-block-start": { + ty: "LengthPercentageOrAuto", + logical_group: { ty: "margin", category: "logical" }, + }, + "margin-block-end": { + ty: "LengthPercentageOrAuto", + logical_group: { ty: "margin", category: "logical" }, + }, + "margin-inline-start": { + ty: "LengthPercentageOrAuto", + logical_group: { ty: "margin", category: "logical" }, + }, + "margin-inline-end": { + ty: "LengthPercentageOrAuto", + logical_group: { ty: "margin", category: "logical" }, + }, + "margin-block": { + ty: "MarginBlock", + shorthand: true, + }, + "margin-inline": { + ty: "MarginInline", + shorthand: true, + }, + margin: { + ty: "Margin", + shorthand: true, + eval_branch_quota: 5000, + }, + "padding-top": { + ty: "LengthPercentageOrAuto", + logical_group: { ty: "padding", category: "physical" }, + }, + "padding-bottom": { + ty: "LengthPercentageOrAuto", + logical_group: { ty: "padding", category: "physical" }, + }, + "padding-left": { + ty: "LengthPercentageOrAuto", + logical_group: { ty: "padding", category: "physical" }, + }, + "padding-right": { + ty: "LengthPercentageOrAuto", + logical_group: { ty: "padding", category: "physical" }, + }, + "padding-block-start": { + ty: "LengthPercentageOrAuto", + logical_group: { ty: "padding", category: "logical" }, + }, + "padding-block-end": { + ty: "LengthPercentageOrAuto", + logical_group: { ty: "padding", category: "logical" }, + }, + "padding-inline-start": { + ty: "LengthPercentageOrAuto", + logical_group: { ty: "padding", category: "logical" }, + }, + "padding-inline-end": { + ty: "LengthPercentageOrAuto", + logical_group: { ty: "padding", category: "logical" }, + }, + "padding-block": { + ty: "PaddingBlock", + shorthand: true, + }, + "padding-inline": { + ty: "PaddingInline", + shorthand: true, + }, + padding: { + ty: "Padding", + shorthand: true, + }, + "scroll-margin-top": { + ty: "LengthPercentageOrAuto", + logical_group: { ty: "scroll_margin", category: "physical" }, + }, + "scroll-margin-bottom": { + ty: "LengthPercentageOrAuto", + logical_group: { ty: "scroll_margin", category: "physical" }, + }, + "scroll-margin-left": { + ty: "LengthPercentageOrAuto", + logical_group: { ty: "scroll_margin", category: "physical" }, + }, + "scroll-margin-right": { + ty: "LengthPercentageOrAuto", + logical_group: { ty: "scroll_margin", category: "physical" }, + }, + "scroll-margin-block-start": { + ty: "LengthPercentageOrAuto", + logical_group: { ty: "scroll_margin", category: "logical" }, + }, + "scroll-margin-block-end": { + ty: "LengthPercentageOrAuto", + logical_group: { ty: "scroll_margin", category: "logical" }, + }, + "scroll-margin-inline-start": { + ty: "LengthPercentageOrAuto", + logical_group: { ty: "scroll_margin", category: "logical" }, + }, + "scroll-margin-inline-end": { + ty: "LengthPercentageOrAuto", + logical_group: { ty: "scroll_margin", category: "logical" }, + }, + "scroll-margin-block": { + ty: "ScrollMarginBlock", + shorthand: true, + }, + "scroll-margin-inline": { + ty: "ScrollMarginInline", + shorthand: true, + }, + "scroll-margin": { + ty: "ScrollMargin", + shorthand: true, + }, + "scroll-padding-top": { + ty: "LengthPercentageOrAuto", + logical_group: { ty: "scroll_padding", category: "physical" }, + }, + "scroll-padding-bottom": { + ty: "LengthPercentageOrAuto", + logical_group: { ty: "scroll_padding", category: "physical" }, + }, + "scroll-padding-left": { + ty: "LengthPercentageOrAuto", + logical_group: { ty: "scroll_padding", category: "physical" }, + }, + "scroll-padding-right": { + ty: "LengthPercentageOrAuto", + logical_group: { ty: "scroll_padding", category: "physical" }, + }, + "scroll-padding-block-start": { + ty: "LengthPercentageOrAuto", + logical_group: { ty: "scroll_padding", category: "logical" }, + }, + "scroll-padding-block-end": { + ty: "LengthPercentageOrAuto", + logical_group: { ty: "scroll_padding", category: "logical" }, + }, + "scroll-padding-inline-start": { + ty: "LengthPercentageOrAuto", + logical_group: { ty: "scroll_padding", category: "logical" }, + }, + "scroll-padding-inline-end": { + ty: "LengthPercentageOrAuto", + logical_group: { ty: "scroll_padding", category: "logical" }, + }, + "scroll-padding-block": { + ty: "ScrollPaddingBlock", + shorthand: true, + }, + "scroll-padding-inline": { + ty: "ScrollPaddingInline", + shorthand: true, + }, + "scroll-padding": { + ty: "ScrollPadding", + shorthand: true, + }, + "font-weight": { + ty: "FontWeight", + }, + "font-size": { + ty: "FontSize", + }, + "font-stretch": { + ty: "FontStretch", + }, + "font-family": { + ty: "BabyList(FontFamily)", + }, + "font-style": { + ty: "FontStyle", + }, + "font-variant-caps": { + ty: "FontVariantCaps", + }, + "line-height": { + ty: "LineHeight", + }, + font: { + ty: "Font", + shorthand: true, + }, // "vertical-align": { // ty: "VerticalAlign", // }, @@ -1293,9 +1422,9 @@ generateCode({ // ty: "TextSizeAdjust", // valid_prefixes: ["webkit", "moz", "ms"], // }, - // direction: { - // ty: "Direction", - // }, + direction: { + ty: "Direction", + }, // "unicode-bidi": { // ty: "UnicodeBidi", // }, @@ -1420,110 +1549,111 @@ generateCode({ // "clip-rule": { // ty: "FillRule", // }, - // "mask-image": { - // ty: "SmallList(Image, 1)", - // valid_prefixes: ["webkit"], - // }, - // "mask-mode": { - // ty: "SmallList(MaskMode, 1)", - // }, - // "mask-repeat": { - // ty: "SmallList(BackgroundRepeat, 1)", - // valid_prefixes: ["webkit"], - // }, - // "mask-position-x": { - // ty: "SmallList(HorizontalPosition, 1)", - // }, - // "mask-position-y": { - // ty: "SmallList(VerticalPosition, 1)", - // }, - // "mask-position": { - // ty: "SmallList(Position, 1)", - // valid_prefixes: ["webkit"], - // }, - // "mask-clip": { - // ty: "SmallList(MaskClip, 1)", - // valid_prefixes: ["webkit"], - // }, - // "mask-origin": { - // ty: "SmallList(GeometryBox, 1)", - // valid_prefixes: ["webkit"], - // }, - // "mask-size": { - // ty: "SmallList(BackgroundSize, 1)", - // valid_prefixes: ["webkit"], - // }, - // "mask-composite": { - // ty: "SmallList(MaskComposite, 1)", - // }, - // "mask-type": { - // ty: "MaskType", - // }, - // mask: { - // ty: "SmallList(Mask, 1)", - // valid_prefixes: ["webkit"], - // shorthand: true, - // }, - // "mask-border-source": { - // ty: "Image", - // }, - // "mask-border-mode": { - // ty: "MaskBorderMode", - // }, - // "mask-border-slice": { - // ty: "BorderImageSlice", - // }, - // "mask-border-width": { - // ty: "Rect(BorderImageSideWidth)", - // }, - // "mask-border-outset": { - // ty: "Rect(LengthOrNumber)", - // }, - // "mask-border-repeat": { - // ty: "BorderImageRepeat", - // }, - // "mask-border": { - // ty: "MaskBorder", - // shorthand: true, - // }, - // "-webkit-mask-composite": { - // ty: "SmallList(WebKitMaskComposite, 1)", - // }, - // "mask-source-type": { - // ty: "SmallList(WebKitMaskSourceType, 1)", - // valid_prefixes: ["webkit"], - // unprefixed: false, - // }, - // "mask-box-image": { - // ty: "BorderImage", - // valid_prefixes: ["webkit"], - // unprefixed: false, - // }, - // "mask-box-image-source": { - // ty: "Image", - // valid_prefixes: ["webkit"], - // unprefixed: false, - // }, - // "mask-box-image-slice": { - // ty: "BorderImageSlice", - // valid_prefixes: ["webkit"], - // unprefixed: false, - // }, - // "mask-box-image-width": { - // ty: "Rect(BorderImageSideWidth)", - // valid_prefixes: ["webkit"], - // unprefixed: false, - // }, - // "mask-box-image-outset": { - // ty: "Rect(LengthOrNumber)", - // valid_prefixes: ["webkit"], - // unprefixed: false, - // }, - // "mask-box-image-repeat": { - // ty: "BorderImageRepeat", - // valid_prefixes: ["webkit"], - // unprefixed: false, - // }, + "mask-image": { + ty: "SmallList(Image, 1)", + valid_prefixes: ["webkit"], + }, + "mask-mode": { + ty: "SmallList(MaskMode, 1)", + }, + "mask-repeat": { + ty: "SmallList(BackgroundRepeat, 1)", + valid_prefixes: ["webkit"], + }, + "mask-position-x": { + ty: "SmallList(HorizontalPosition, 1)", + }, + "mask-position-y": { + ty: "SmallList(VerticalPosition, 1)", + }, + "mask-position": { + ty: "SmallList(Position, 1)", + valid_prefixes: ["webkit"], + }, + "mask-clip": { + ty: "SmallList(MaskClip, 1)", + valid_prefixes: ["webkit"], + eval_branch_quota: 5000, + }, + "mask-origin": { + ty: "SmallList(GeometryBox, 1)", + valid_prefixes: ["webkit"], + }, + "mask-size": { + ty: "SmallList(BackgroundSize, 1)", + valid_prefixes: ["webkit"], + }, + "mask-composite": { + ty: "SmallList(MaskComposite, 1)", + }, + "mask-type": { + ty: "MaskType", + }, + mask: { + ty: "SmallList(Mask, 1)", + valid_prefixes: ["webkit"], + shorthand: true, + }, + "mask-border-source": { + ty: "Image", + }, + "mask-border-mode": { + ty: "MaskBorderMode", + }, + "mask-border-slice": { + ty: "BorderImageSlice", + }, + "mask-border-width": { + ty: "Rect(BorderImageSideWidth)", + }, + "mask-border-outset": { + ty: "Rect(LengthOrNumber)", + }, + "mask-border-repeat": { + ty: "BorderImageRepeat", + }, + "mask-border": { + ty: "MaskBorder", + shorthand: true, + }, + "-webkit-mask-composite": { + ty: "SmallList(WebKitMaskComposite, 1)", + }, + "mask-source-type": { + ty: "SmallList(WebKitMaskSourceType, 1)", + valid_prefixes: ["webkit"], + unprefixed: false, + }, + "mask-box-image": { + ty: "BorderImage", + valid_prefixes: ["webkit"], + unprefixed: false, + }, + "mask-box-image-source": { + ty: "Image", + valid_prefixes: ["webkit"], + unprefixed: false, + }, + "mask-box-image-slice": { + ty: "BorderImageSlice", + valid_prefixes: ["webkit"], + unprefixed: false, + }, + "mask-box-image-width": { + ty: "Rect(BorderImageSideWidth)", + valid_prefixes: ["webkit"], + unprefixed: false, + }, + "mask-box-image-outset": { + ty: "Rect(LengthOrNumber)", + valid_prefixes: ["webkit"], + unprefixed: false, + }, + "mask-box-image-repeat": { + ty: "BorderImageRepeat", + valid_prefixes: ["webkit"], + unprefixed: false, + }, // filter: { // ty: "FilterList", // valid_prefixes: ["webkit"], @@ -1582,7 +1712,9 @@ const LengthPercentageOrAuto = css_values.length.LengthPercentageOrAuto; const PropertyCategory = css.PropertyCategory; const LogicalGroup = css.LogicalGroup; const CSSNumber = css.css_values.number.CSSNumber; +const CSSNumberFns = css.css_values.number.CSSNumberFns; const CSSInteger = css.css_values.number.CSSInteger; +const CSSIntegerFns = css.css_values.number.CSSIntegerFns; const NumberOrPercentage = css.css_values.percentage.NumberOrPercentage; const Percentage = css.css_values.percentage.Percentage; const Angle = css.css_values.angle.Angle; @@ -1655,51 +1787,51 @@ const BorderInlineStart = border.BorderInlineStart; const BorderInlineEnd = border.BorderInlineEnd; const BorderBlock = border.BorderBlock; const BorderInline = border.BorderInline; -// const Outline = outline.Outline; -// const OutlineStyle = outline.OutlineStyle; -// const FlexDirection = flex.FlexDirection; -// const FlexWrap = flex.FlexWrap; -// const FlexFlow = flex.FlexFlow; -// const Flex = flex.Flex; -// const BoxOrient = flex.BoxOrient; -// const BoxDirection = flex.BoxDirection; -// const BoxAlign = flex.BoxAlign; -// const BoxPack = flex.BoxPack; -// const BoxLines = flex.BoxLines; -// const FlexPack = flex.FlexPack; -// const FlexItemAlign = flex.FlexItemAlign; -// const FlexLinePack = flex.FlexLinePack; -// const AlignContent = @"align".AlignContent; -// const JustifyContent = @"align".JustifyContent; -// const PlaceContent = @"align".PlaceContent; -// const AlignSelf = @"align".AlignSelf; -// const JustifySelf = @"align".JustifySelf; -// const PlaceSelf = @"align".PlaceSelf; -// const AlignItems = @"align".AlignItems; -// const JustifyItems = @"align".JustifyItems; -// const PlaceItems = @"align".PlaceItems; -// const GapValue = @"align".GapValue; -// const Gap = @"align".Gap; -// const MarginBlock = margin_padding.MarginBlock; -// const Margin = margin_padding.Margin; -// const MarginInline = margin_padding.MarginInline; -// const PaddingBlock = margin_padding.PaddingBlock; -// const PaddingInline = margin_padding.PaddingInline; -// const Padding = margin_padding.Padding; -// const ScrollMarginBlock = margin_padding.ScrollMarginBlock; -// const ScrollMarginInline = margin_padding.ScrollMarginInline; -// const ScrollMargin = margin_padding.ScrollMargin; -// const ScrollPaddingBlock = margin_padding.ScrollPaddingBlock; -// const ScrollPaddingInline = margin_padding.ScrollPaddingInline; -// const ScrollPadding = margin_padding.ScrollPadding; -// const FontWeight = font.FontWeight; -// const FontSize = font.FontSize; -// const FontStretch = font.FontStretch; -// const FontFamily = font.FontFamily; -// const FontStyle = font.FontStyle; -// const FontVariantCaps = font.FontVariantCaps; -// const LineHeight = font.LineHeight; -// const Font = font.Font; +const Outline = outline.Outline; +const OutlineStyle = outline.OutlineStyle; +const FlexDirection = flex.FlexDirection; +const FlexWrap = flex.FlexWrap; +const FlexFlow = flex.FlexFlow; +const Flex = flex.Flex; +const BoxOrient = flex.BoxOrient; +const BoxDirection = flex.BoxDirection; +const BoxAlign = flex.BoxAlign; +const BoxPack = flex.BoxPack; +const BoxLines = flex.BoxLines; +const FlexPack = flex.FlexPack; +const FlexItemAlign = flex.FlexItemAlign; +const FlexLinePack = flex.FlexLinePack; +const AlignContent = @"align".AlignContent; +const JustifyContent = @"align".JustifyContent; +const PlaceContent = @"align".PlaceContent; +const AlignSelf = @"align".AlignSelf; +const JustifySelf = @"align".JustifySelf; +const PlaceSelf = @"align".PlaceSelf; +const AlignItems = @"align".AlignItems; +const JustifyItems = @"align".JustifyItems; +const PlaceItems = @"align".PlaceItems; +const GapValue = @"align".GapValue; +const Gap = @"align".Gap; +const MarginBlock = margin_padding.MarginBlock; +const Margin = margin_padding.Margin; +const MarginInline = margin_padding.MarginInline; +const PaddingBlock = margin_padding.PaddingBlock; +const PaddingInline = margin_padding.PaddingInline; +const Padding = margin_padding.Padding; +const ScrollMarginBlock = margin_padding.ScrollMarginBlock; +const ScrollMarginInline = margin_padding.ScrollMarginInline; +const ScrollMargin = margin_padding.ScrollMargin; +const ScrollPaddingBlock = margin_padding.ScrollPaddingBlock; +const ScrollPaddingInline = margin_padding.ScrollPaddingInline; +const ScrollPadding = margin_padding.ScrollPadding; +const FontWeight = font.FontWeight; +const FontSize = font.FontSize; +const FontStretch = font.FontStretch; +const FontFamily = font.FontFamily; +const FontStyle = font.FontStyle; +const FontVariantCaps = font.FontVariantCaps; +const LineHeight = font.LineHeight; +const Font = font.Font; // const VerticalAlign = font.VerticalAlign; // const Transition = transition.Transition; // const AnimationNameList = animation.AnimationNameList; @@ -1744,7 +1876,7 @@ const BorderInline = border.BorderInline; // const TextEmphasisPosition = text.TextEmphasisPosition; // const TextShadow = text.TextShadow; // const TextSizeAdjust = text.TextSizeAdjust; -// const Direction = text.Direction; +const Direction = text.Direction; // const UnicodeBidi = text.UnicodeBidi; // const BoxDecorationBreak = text.BoxDecorationBreak; // const Resize = ui.Resize; @@ -1772,30 +1904,31 @@ const Composes = css_modules.Composes; // const ShapeRendering = svg.ShapeRendering; // const TextRendering = svg.TextRendering; // const ImageRendering = svg.ImageRendering; -// const ClipPath = masking.ClipPath; -// const MaskMode = masking.MaskMode; -// const MaskClip = masking.MaskClip; -// const GeometryBox = masking.GeometryBox; -// const MaskComposite = masking.MaskComposite; -// const MaskType = masking.MaskType; -// const Mask = masking.Mask; -// const MaskBorderMode = masking.MaskBorderMode; -// const MaskBorder = masking.MaskBorder; -// const WebKitMaskComposite = masking.WebKitMaskComposite; -// const WebKitMaskSourceType = masking.WebKitMaskSourceType; -// const BackgroundRepeat = background.BackgroundRepeat; -// const BackgroundSize = background.BackgroundSize; +const ClipPath = masking.ClipPath; +const MaskMode = masking.MaskMode; +const MaskClip = masking.MaskClip; +const GeometryBox = masking.GeometryBox; +const MaskComposite = masking.MaskComposite; +const MaskType = masking.MaskType; +const Mask = masking.Mask; +const MaskBorderMode = masking.MaskBorderMode; +const MaskBorder = masking.MaskBorder; +const WebKitMaskComposite = masking.WebKitMaskComposite; +const WebKitMaskSourceType = masking.WebKitMaskSourceType; +const BackgroundRepeat = background.BackgroundRepeat; +const BackgroundSize = background.BackgroundSize; // const FilterList = effects.FilterList; // const ContainerType = contain.ContainerType; // const Container = contain.Container; // const ContainerNameList = contain.ContainerNameList; const CustomPropertyName = custom.CustomPropertyName; -// const display = css.css_properties.display; +const display = css.css_properties.display; const Position = position.Position; const Result = css.Result; +const BabyList = bun.BabyList; const ArrayList = std.ArrayListUnmanaged; const SmallList = css.SmallList; diff --git a/src/css/properties/margin_padding.zig b/src/css/properties/margin_padding.zig index ff77a06207..fedfb79890 100644 --- a/src/css/properties/margin_padding.zig +++ b/src/css/properties/margin_padding.zig @@ -36,7 +36,8 @@ pub const Inset = struct { bottom: LengthPercentageOrAuto, left: LengthPercentageOrAuto, - pub usingnamespace css.DefineShorthand(@This(), css.PropertyIdTag.inset); + // TODO: bring this back + // pub usingnamespace css.DefineShorthand(@This(), css.PropertyIdTag.inset); pub usingnamespace css.DefineRectShorthand(@This(), LengthPercentageOrAuto); pub const PropertyFieldMap = .{ @@ -45,6 +46,14 @@ pub const Inset = struct { .bottom = css.PropertyIdTag.bottom, .left = css.PropertyIdTag.left, }; + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } }; /// A value for the [inset-block](https://drafts.csswg.org/css-logical/#propdef-inset-block) shorthand property. @@ -54,13 +63,22 @@ pub const InsetBlock = struct { /// The block end value. block_end: LengthPercentageOrAuto, - pub usingnamespace css.DefineShorthand(@This(), css.PropertyIdTag.@"inset-block"); + // TODO: bring this back + // pub usingnamespace css.DefineShorthand(@This(), css.PropertyIdTag.@"inset-block"); pub usingnamespace css.DefineSizeShorthand(@This(), LengthPercentageOrAuto); pub const PropertyFieldMap = .{ .block_start = css.PropertyIdTag.@"inset-block-start", .block_end = css.PropertyIdTag.@"inset-block-end", }; + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } }; /// A value for the [inset-inline](https://drafts.csswg.org/css-logical/#propdef-inset-inline) shorthand property. @@ -75,8 +93,17 @@ pub const InsetInline = struct { .inline_end = css.PropertyIdTag.@"inset-inline-end", }; - pub usingnamespace css.DefineShorthand(@This(), css.PropertyIdTag.@"inset-inline"); + // TODO: bring this back + // pub usingnamespace css.DefineShorthand(@This(), css.PropertyIdTag.@"inset-inline"); pub usingnamespace css.DefineSizeShorthand(@This(), LengthPercentageOrAuto); + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } }; /// A value for the [margin-block](https://drafts.csswg.org/css-logical/#propdef-margin-block) shorthand property. @@ -86,13 +113,22 @@ pub const MarginBlock = struct { /// The block end value. block_end: LengthPercentageOrAuto, - pub usingnamespace css.DefineShorthand(@This(), css.PropertyIdTag.@"margin-block"); + // TODO: bring this back + // pub usingnamespace css.DefineShorthand(@This(), css.PropertyIdTag.@"margin-block"); pub usingnamespace css.DefineSizeShorthand(@This(), LengthPercentageOrAuto); pub const PropertyFieldMap = .{ .block_start = css.PropertyIdTag.@"margin-block-start", .block_end = css.PropertyIdTag.@"margin-block-end", }; + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } }; /// A value for the [margin-inline](https://drafts.csswg.org/css-logical/#propdef-margin-inline) shorthand property. @@ -102,13 +138,22 @@ pub const MarginInline = struct { /// The inline end value. inline_end: LengthPercentageOrAuto, - pub usingnamespace css.DefineShorthand(@This(), css.PropertyIdTag.@"margin-inline"); + // TODO: bring this back + // pub usingnamespace css.DefineShorthand(@This(), css.PropertyIdTag.@"margin-inline"); pub usingnamespace css.DefineSizeShorthand(@This(), LengthPercentageOrAuto); pub const PropertyFieldMap = .{ .inline_start = css.PropertyIdTag.@"margin-inline-start", .inline_end = css.PropertyIdTag.@"margin-inline-end", }; + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } }; /// A value for the [margin](https://drafts.csswg.org/css-box-4/#propdef-margin) shorthand property. @@ -118,7 +163,8 @@ pub const Margin = struct { bottom: LengthPercentageOrAuto, left: LengthPercentageOrAuto, - pub usingnamespace css.DefineShorthand(@This(), css.PropertyIdTag.margin); + // TODO: bring this back + // pub usingnamespace css.DefineShorthand(@This(), css.PropertyIdTag.margin); pub usingnamespace css.DefineRectShorthand(@This(), LengthPercentageOrAuto); pub const PropertyFieldMap = .{ @@ -127,6 +173,14 @@ pub const Margin = struct { .bottom = css.PropertyIdTag.@"margin-bottom", .left = css.PropertyIdTag.@"margin-left", }; + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } }; /// A value for the [padding-block](https://drafts.csswg.org/css-logical/#propdef-padding-block) shorthand property. @@ -136,13 +190,22 @@ pub const PaddingBlock = struct { /// The block end value. block_end: LengthPercentageOrAuto, - pub usingnamespace css.DefineShorthand(@This(), css.PropertyIdTag.@"padding-block"); + // TODO: bring this back + // pub usingnamespace css.DefineShorthand(@This(), css.PropertyIdTag.@"padding-block"); pub usingnamespace css.DefineSizeShorthand(@This(), LengthPercentageOrAuto); pub const PropertyFieldMap = .{ .block_start = css.PropertyIdTag.@"padding-block-start", .block_end = css.PropertyIdTag.@"padding-block-end", }; + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } }; /// A value for the [padding-inline](https://drafts.csswg.org/css-logical/#propdef-padding-inline) shorthand property. @@ -152,13 +215,22 @@ pub const PaddingInline = struct { /// The inline end value. inline_end: LengthPercentageOrAuto, - pub usingnamespace css.DefineShorthand(@This(), css.PropertyIdTag.@"padding-inline"); + // TODO: bring this back + // pub usingnamespace css.DefineShorthand(@This(), css.PropertyIdTag.@"padding-inline"); pub usingnamespace css.DefineSizeShorthand(@This(), LengthPercentageOrAuto); pub const PropertyFieldMap = .{ .inline_start = css.PropertyIdTag.@"padding-inline-start", .inline_end = css.PropertyIdTag.@"padding-inline-end", }; + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } }; /// A value for the [padding](https://drafts.csswg.org/css-box-4/#propdef-padding) shorthand property. @@ -168,7 +240,8 @@ pub const Padding = struct { bottom: LengthPercentageOrAuto, left: LengthPercentageOrAuto, - pub usingnamespace css.DefineShorthand(@This(), css.PropertyIdTag.padding); + // TODO: bring this back + // pub usingnamespace css.DefineShorthand(@This(), css.PropertyIdTag.padding); pub usingnamespace css.DefineRectShorthand(@This(), LengthPercentageOrAuto); pub const PropertyFieldMap = .{ @@ -177,6 +250,14 @@ pub const Padding = struct { .bottom = css.PropertyIdTag.@"padding-bottom", .left = css.PropertyIdTag.@"padding-left", }; + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } }; /// A value for the [scroll-margin-block](https://drafts.csswg.org/css-scroll-snap/#propdef-scroll-margin-block) shorthand property. @@ -186,13 +267,22 @@ pub const ScrollMarginBlock = struct { /// The block end value. block_end: LengthPercentageOrAuto, - pub usingnamespace css.DefineShorthand(@This(), css.PropertyIdTag.@"scroll-margin-block"); + // TODO: bring this back + // pub usingnamespace css.DefineShorthand(@This(), css.PropertyIdTag.@"scroll-margin-block"); pub usingnamespace css.DefineSizeShorthand(@This(), LengthPercentageOrAuto); pub const PropertyFieldMap = .{ .block_start = css.PropertyIdTag.@"scroll-margin-block-start", .block_end = css.PropertyIdTag.@"scroll-margin-block-end", }; + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } }; /// A value for the [scroll-margin-inline](https://drafts.csswg.org/css-scroll-snap/#propdef-scroll-margin-inline) shorthand property. @@ -202,13 +292,22 @@ pub const ScrollMarginInline = struct { /// The inline end value. inline_end: LengthPercentageOrAuto, - pub usingnamespace css.DefineShorthand(@This(), css.PropertyIdTag.@"scroll-margin-inline"); + // TODO: bring this back + // pub usingnamespace css.DefineShorthand(@This(), css.PropertyIdTag.@"scroll-margin-inline"); pub usingnamespace css.DefineSizeShorthand(@This(), LengthPercentageOrAuto); pub const PropertyFieldMap = .{ .inline_start = css.PropertyIdTag.@"scroll-margin-inline-start", .inline_end = css.PropertyIdTag.@"scroll-margin-inline-end", }; + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } }; /// A value for the [scroll-margin](https://drafts.csswg.org/css-scroll-snap/#scroll-margin) shorthand property. @@ -218,7 +317,8 @@ pub const ScrollMargin = struct { bottom: LengthPercentageOrAuto, left: LengthPercentageOrAuto, - pub usingnamespace css.DefineShorthand(@This(), css.PropertyIdTag.@"scroll-margin"); + // TODO: bring this back + // pub usingnamespace css.DefineShorthand(@This(), css.PropertyIdTag.@"scroll-margin"); pub usingnamespace css.DefineRectShorthand(@This(), LengthPercentageOrAuto); pub const PropertyFieldMap = .{ @@ -227,6 +327,14 @@ pub const ScrollMargin = struct { .bottom = css.PropertyIdTag.@"scroll-margin-bottom", .left = css.PropertyIdTag.@"scroll-margin-left", }; + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } }; /// A value for the [scroll-padding-block](https://drafts.csswg.org/css-scroll-snap/#propdef-scroll-padding-block) shorthand property. @@ -236,13 +344,22 @@ pub const ScrollPaddingBlock = struct { /// The block end value. block_end: LengthPercentageOrAuto, - pub usingnamespace css.DefineShorthand(@This(), css.PropertyIdTag.@"scroll-padding-block"); + // TODO: bring this back + // pub usingnamespace css.DefineShorthand(@This(), css.PropertyIdTag.@"scroll-padding-block"); pub usingnamespace css.DefineSizeShorthand(@This(), LengthPercentageOrAuto); pub const PropertyFieldMap = .{ .block_start = css.PropertyIdTag.@"scroll-padding-block-start", .block_end = css.PropertyIdTag.@"scroll-padding-block-end", }; + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } }; /// A value for the [scroll-padding-inline](https://drafts.csswg.org/css-scroll-snap/#propdef-scroll-padding-inline) shorthand property. @@ -252,13 +369,22 @@ pub const ScrollPaddingInline = struct { /// The inline end value. inline_end: LengthPercentageOrAuto, - pub usingnamespace css.DefineShorthand(@This(), css.PropertyIdTag.@"scroll-padding-inline"); + // TODO: bring this back + // pub usingnamespace css.DefineShorthand(@This(), css.PropertyIdTag.@"scroll-padding-inline"); pub usingnamespace css.DefineSizeShorthand(@This(), LengthPercentageOrAuto); pub const PropertyFieldMap = .{ .inline_start = css.PropertyIdTag.@"scroll-padding-inline-start", .inline_end = css.PropertyIdTag.@"scroll-padding-inline-end", }; + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } }; /// A value for the [scroll-padding](https://drafts.csswg.org/css-scroll-snap/#scroll-padding) shorthand property. @@ -268,7 +394,8 @@ pub const ScrollPadding = struct { bottom: LengthPercentageOrAuto, left: LengthPercentageOrAuto, - pub usingnamespace css.DefineShorthand(@This(), css.PropertyIdTag.@"scroll-padding"); + // TODO: bring this back + // pub usingnamespace css.DefineShorthand(@This(), css.PropertyIdTag.@"scroll-padding"); pub usingnamespace css.DefineRectShorthand(@This(), LengthPercentageOrAuto); pub const PropertyFieldMap = .{ @@ -277,4 +404,12 @@ pub const ScrollPadding = struct { .bottom = css.PropertyIdTag.@"scroll-padding-bottom", .left = css.PropertyIdTag.@"scroll-padding-left", }; + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } }; diff --git a/src/css/properties/masking.zig b/src/css/properties/masking.zig index 8511c1a37e..e4d1573ef8 100644 --- a/src/css/properties/masking.zig +++ b/src/css/properties/masking.zig @@ -28,11 +28,19 @@ const NumberOrPercentage = css.css_values.percentage.NumberOrPercentage; const CustomIdentList = css.css_values.ident.CustomIdentList; const Angle = css.css_values.angle.Angle; const Url = css.css_values.url.Url; +const LengthOrNumber = css.css_values.length.LengthOrNumber; +const Position = css.css_values.position.Position; -const Position = css.css_properties.position.Position; const BorderRadius = css.css_properties.border_radius.BorderRadius; const FillRule = css.css_properties.shape.FillRule; +const BackgroundSize = css.css_properties.background.BackgroundSize; +const BackgroundRepeat = css.css_properties.background.BackgroundRepeat; +const BorderImageSlice = css.css_properties.border_image.BorderImageSlice; +const BorderImageSideWidth = css.css_properties.border_image.BorderImageSideWidth; +const BorderImageRepeat = css.css_properties.border_image.BorderImageRepeat; +const BorderImage = css.css_properties.border_image.BorderImage; + /// A value for the [clip-path](https://www.w3.org/TR/css-masking-1/#the-clip-path) property. const ClipPath = union(enum) { /// No clip path. @@ -53,10 +61,35 @@ const ClipPath = union(enum) { /// A [``](https://www.w3.org/TR/css-masking-1/#typedef-geometry-box) value /// as used in the `mask-clip` and `clip-path` properties. -const GeometryBox = css.DefineEnumProperty(@compileError(css.todo_stuff.depth)); +pub const GeometryBox = enum { + /// The painted content is clipped to the content box. + @"border-box", + /// The painted content is clipped to the padding box. + @"padding-box", + /// The painted content is clipped to the border box. + @"content-box", + /// The painted content is clipped to the margin box. + @"margin-box", + /// The painted content is clipped to the object bounding box. + @"fill-box", + /// The painted content is clipped to the stroke bounding box. + @"stroke-box", + /// Uses the nearest SVG viewport as reference box. + @"view-box", + + pub usingnamespace css.DefineEnumProperty(@This()); + + pub fn intoMaskClip(this: *const @This()) MaskClip { + return MaskClip{ .@"geometry-box" = this.* }; + } + + pub fn default() GeometryBox { + return .@"border-box"; + } +}; /// A CSS [``](https://www.w3.org/TR/css-shapes-1/#basic-shape-functions) value. -const BasicShape = union(enum) { +pub const BasicShape = union(enum) { /// An inset rectangle. Inset: InsetRect, /// A circle. @@ -123,39 +156,386 @@ pub const Point = struct { }; /// A value for the [mask-mode](https://www.w3.org/TR/css-masking-1/#the-mask-mode) property. -const MaskMode = css.DefineEnumProperty(@compileError(css.todo_stuff.depth)); +pub const MaskMode = enum { + /// The luminance values of the mask image is used. + luminance, + /// The alpha values of the mask image is used. + alpha, + /// If an SVG source is used, the value matches the `mask-type` property. Otherwise, the alpha values are used. + @"match-source", + + pub usingnamespace css.DefineEnumProperty(@This()); + + pub fn default() MaskMode { + return .@"match-source"; + } +}; /// A value for the [mask-clip](https://www.w3.org/TR/css-masking-1/#the-mask-clip) property. -const MaskClip = union(enum) { +pub const MaskClip = union(enum) { /// A geometry box. - GeometryBox: GeometryBox, + @"geometry-box": GeometryBox, /// The painted content is not clipped. - NoClip, + @"no-clip", + + pub usingnamespace @call(.auto, css.DeriveParse, .{@This()}); + pub usingnamespace @call(.auto, css.DeriveToCss, .{@This()}); + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } }; /// A value for the [mask-composite](https://www.w3.org/TR/css-masking-1/#the-mask-composite) property. -pub const MaskComposite = css.DefineEnumProperty(@compileError(css.todo_stuff.depth)); +pub const MaskComposite = enum { + /// The source is placed over the destination. + add, + /// The source is placed, where it falls outside of the destination. + subtract, + /// The parts of source that overlap the destination, replace the destination. + intersect, + /// The non-overlapping regions of source and destination are combined. + exclude, + + pub usingnamespace css.DefineEnumProperty(@This()); + + pub fn default() MaskComposite { + return .add; + } +}; /// A value for the [mask-type](https://www.w3.org/TR/css-masking-1/#the-mask-type) property. -pub const MaskType = css.DefineEnumProperty(@compileError(css.todo_stuff.depth)); +pub const MaskType = enum { + /// The luminance values of the mask is used. + luminance, + /// The alpha values of the mask is used. + alpha, + + pub usingnamespace css.DefineEnumProperty(@This()); +}; /// A value for the [mask](https://www.w3.org/TR/css-masking-1/#the-mask) shorthand property. -pub const Mask = @compileError(css.todo_stuff.depth); +pub const Mask = struct { + /// The mask image. + image: Image, + /// The position of the mask. + position: Position, + /// The size of the mask image. + size: BackgroundSize, + /// How the mask repeats. + repeat: BackgroundRepeat, + /// The box in which the mask is clipped. + clip: MaskClip, + /// The origin of the mask. + origin: GeometryBox, + /// How the mask is composited with the element. + composite: MaskComposite, + /// How the mask image is interpreted. + mode: MaskMode, + + pub usingnamespace css.DefineListShorthand(@This()); + + pub const PropertyFieldMap = .{ + .image = css.PropertyIdTag.@"mask-image", + .position = css.PropertyIdTag.@"mask-position", + .size = css.PropertyIdTag.@"mask-size", + .repeat = css.PropertyIdTag.@"mask-repeat", + .clip = css.PropertyIdTag.@"mask-clip", + .origin = css.PropertyIdTag.@"mask-origin", + .composite = css.PropertyIdTag.@"mask-composite", + .mode = css.PropertyIdTag.@"mask-mode", + }; + + pub const VendorPrefixMap = .{ + .image = true, + .position = true, + .size = true, + .repeat = true, + .clip = true, + .origin = true, + }; + + pub fn parse(input: *css.Parser) css.Result(@This()) { + var image: ?Image = null; + var position: ?Position = null; + var size: ?BackgroundSize = null; + var repeat: ?BackgroundRepeat = null; + var clip: ?MaskClip = null; + var origin: ?GeometryBox = null; + var composite: ?MaskComposite = null; + var mode: ?MaskMode = null; + + while (true) { + if (image == null) { + if (@call(.auto, @field(Image, "parse"), .{input}).asValue()) |value| { + image = value; + continue; + } + } + + if (position == null) { + if (Position.parse(input).asValue()) |value| { + position = value; + size = input.tryParse(struct { + pub inline fn parseFn(i: *css.Parser) css.Result(BackgroundSize) { + if (i.expectDelim('/').asErr()) |e| return .{ .err = e }; + return BackgroundSize.parse(i); + } + }.parseFn, .{}).asValue(); + continue; + } + } + + if (repeat == null) { + if (BackgroundRepeat.parse(input).asValue()) |value| { + repeat = value; + continue; + } + } + + if (origin == null) { + if (GeometryBox.parse(input).asValue()) |value| { + origin = value; + continue; + } + } + + if (clip == null) { + if (MaskClip.parse(input).asValue()) |value| { + clip = value; + continue; + } + } + + if (composite == null) { + if (MaskComposite.parse(input).asValue()) |value| { + composite = value; + continue; + } + } + + if (mode == null) { + if (MaskMode.parse(input).asValue()) |value| { + mode = value; + continue; + } + } + + break; + } + + if (clip == null) { + if (origin) |o| { + clip = o.intoMaskClip(); + } + } + + return .{ .result = .{ + .image = image orelse Image.default(), + .position = position orelse Position.default(), + .repeat = repeat orelse BackgroundRepeat.default(), + .size = size orelse BackgroundSize.default(), + .origin = origin orelse .@"border-box", + .clip = clip orelse GeometryBox.@"border-box".intoMaskClip(), + .composite = composite orelse .add, + .mode = mode orelse .@"match-source", + } }; + } + + pub fn toCss(this: *const Mask, comptime W: type, dest: *css.Printer(W)) css.PrintErr!void { + try this.image.toCss(W, dest); + + if (!this.position.eql(&Position.default()) or !this.size.eql(&BackgroundSize.default())) { + try dest.writeChar(' '); + try this.position.toCss(W, dest); + + if (!this.size.eql(&BackgroundSize.default())) { + try dest.delim('/', true); + try this.size.toCss(W, dest); + } + } + + if (!this.repeat.eql(&BackgroundRepeat.default())) { + try dest.writeChar(' '); + try this.repeat.toCss(W, dest); + } + + if (!this.origin.eql(&GeometryBox.@"border-box") or !this.clip.eql(&GeometryBox.@"border-box".intoMaskClip())) { + try dest.writeChar(' '); + try this.origin.toCss(W, dest); + + if (!this.clip.eql(&this.origin.intoMaskClip())) { + try dest.writeChar(' '); + try this.clip.toCss(W, dest); + } + } + + if (!this.composite.eql(&MaskComposite.default())) { + try dest.writeChar(' '); + try this.composite.toCss(W, dest); + } + + if (!this.mode.eql(&MaskMode.default())) { + try dest.writeChar(' '); + try this.mode.toCss(W, dest); + } + + return; + } + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } +}; /// A value for the [mask-border-mode](https://www.w3.org/TR/css-masking-1/#the-mask-border-mode) property. -pub const MaskBorderMode = css.DefineEnumProperty(@compileError(css.todo_stuff.depth)); +pub const MaskBorderMode = enum { + /// The luminance values of the mask image is used. + luminance, + /// The alpha values of the mask image is used. + alpha, + + pub usingnamespace css.DefineEnumProperty(@This()); + + pub fn default() @This() { + return .alpha; + } +}; /// A value for the [mask-border](https://www.w3.org/TR/css-masking-1/#the-mask-border) shorthand property. -pub const MaskBorder = @compileError(css.todo_stuff.depth); +/// A value for the [mask-border](https://www.w3.org/TR/css-masking-1/#the-mask-border) shorthand property. +pub const MaskBorder = struct { + /// The mask image. + source: Image, + /// The offsets that define where the image is sliced. + slice: BorderImageSlice, + /// The width of the mask image. + width: Rect(BorderImageSideWidth), + /// The amount that the image extends beyond the border box. + outset: Rect(LengthOrNumber), + /// How the mask image is scaled and tiled. + repeat: BorderImageRepeat, + /// How the mask image is interpreted. + mode: MaskBorderMode, + + pub usingnamespace css.DefineShorthand(@This(), css.PropertyIdTag.@"mask-border"); + + pub const PropertyFieldMap = .{ + .source = css.PropertyIdTag.@"mask-border-source", + .slice = css.PropertyIdTag.@"mask-border-slice", + .width = css.PropertyIdTag.@"mask-border-width", + .outset = css.PropertyIdTag.@"mask-border-outset", + .repeat = css.PropertyIdTag.@"mask-border-repeat", + .mode = css.PropertyIdTag.@"mask-border-mode", + }; + + pub fn parse(input: *css.Parser) css.Result(@This()) { + const Closure = struct { + mode: ?MaskBorderMode = null, + }; + var closure = Closure{ .mode = null }; + const border_image = BorderImage.parseWithCallback(input, &closure, struct { + inline fn callback(c: *Closure, p: *css.Parser) bool { + if (c.mode == null) { + if (p.tryParse(MaskBorderMode.parse, .{}).asValue()) |value| { + c.mode = value; + return true; + } + } + return false; + } + }.callback); + + if (border_image.isOk() or closure.mode != null) { + const bi = border_image.unwrapOr(comptime BorderImage.default()); + return .{ .result = MaskBorder{ + .source = bi.source, + .slice = bi.slice, + .width = bi.width, + .outset = bi.outset, + .repeat = bi.repeat, + .mode = closure.mode orelse MaskBorderMode.default(), + } }; + } else { + return .{ .err = input.newCustomError(.invalid_declaration) }; + } + } + + pub fn toCss(this: *const MaskBorder, comptime W: type, dest: *css.Printer(W)) css.PrintErr!void { + try BorderImage.toCssInternal( + &this.source, + &this.slice, + &this.width, + &this.outset, + &this.repeat, + W, + dest, + ); + if (!this.mode.eql(&MaskBorderMode.default())) { + try dest.writeChar(' '); + try this.mode.toCss(W, dest); + } + } + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } +}; /// A value for the [-webkit-mask-composite](https://developer.mozilla.org/en-US/docs/Web/CSS/-webkit-mask-composite) /// property. /// /// See also [MaskComposite](MaskComposite). -pub const WebKitMaskComposite = css.DefineEnumProperty(@compileError(css.todo_stuff.depth)); +/// A value for the [-webkit-mask-composite](https://developer.mozilla.org/en-US/docs/Web/CSS/-webkit-mask-composite) +/// property. +/// +/// See also [MaskComposite](MaskComposite). +pub const WebKitMaskComposite = enum { + clear, + copy, + /// Equivalent to `add` in the standard `mask-composite` syntax. + @"source-over", + /// Equivalent to `intersect` in the standard `mask-composite` syntax. + @"source-in", + /// Equivalent to `subtract` in the standard `mask-composite` syntax. + @"source-out", + @"source-atop", + @"destination-over", + @"destination-in", + @"destination-out", + @"destination-atop", + /// Equivalent to `exclude` in the standard `mask-composite` syntax. + xor, + + pub usingnamespace css.DefineEnumProperty(@This()); +}; /// A value for the [-webkit-mask-source-type](https://github.com/WebKit/WebKit/blob/6eece09a1c31e47489811edd003d1e36910e9fd3/Source/WebCore/css/CSSProperties.json#L6578-L6587) /// property. /// /// See also [MaskMode](MaskMode). -pub const WebKitMaskSourceType = css.DefineEnumProperty(@compileError(css.todo_stuff.depth)); +/// A value for the [-webkit-mask-source-type](https://github.com/WebKit/WebKit/blob/6eece09a1c31e47489811edd003d1e36910e9fd3/Source/WebCore/css/CSSProperties.json#L6578-L6587) +/// property. +/// +/// See also [MaskMode](MaskMode). +pub const WebKitMaskSourceType = enum { + /// Equivalent to `match-source` in the standard `mask-mode` syntax. + auto, + /// The luminance values of the mask image is used. + luminance, + /// The alpha values of the mask image is used. + alpha, + + pub usingnamespace css.DefineEnumProperty(@This()); +}; diff --git a/src/css/properties/outline.zig b/src/css/properties/outline.zig index d19b7cb70d..cf98f18c6f 100644 --- a/src/css/properties/outline.zig +++ b/src/css/properties/outline.zig @@ -41,4 +41,19 @@ pub const OutlineStyle = union(enum) { auto: void, /// A value equivalent to the `border-style` property. line_style: LineStyle, + + pub usingnamespace css.DeriveParse(@This()); + pub usingnamespace css.DeriveToCss(@This()); + + pub fn default() @This() { + return .{ .line_style = .none }; + } + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } }; diff --git a/src/css/properties/overflow.zig b/src/css/properties/overflow.zig index fc56a7e479..39b246b9d4 100644 --- a/src/css/properties/overflow.zig +++ b/src/css/properties/overflow.zig @@ -40,7 +40,10 @@ pub const Overflow = struct { y: OverflowKeyword, pub fn parse(input: *css.Parser) css.Result(Overflow) { - const x = try OverflowKeyword.parse(input); + const x = switch (OverflowKeyword.parse(input)) { + .result => |v| v, + .err => |e| return .{ .err = e }, + }; const y = switch (input.tryParse(OverflowKeyword.parse, .{})) { .result => |v| v, else => x, @@ -55,6 +58,14 @@ pub const Overflow = struct { try this.y.toCss(W, dest); } } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } + + pub inline fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } }; /// An [overflow](https://www.w3.org/TR/css-overflow-3/#overflow-properties) keyword diff --git a/src/css/properties/position.zig b/src/css/properties/position.zig index 8c311b64c2..2eeb39147b 100644 --- a/src/css/properties/position.zig +++ b/src/css/properties/position.zig @@ -44,4 +44,64 @@ pub const Position = union(enum) { sticky: css.VendorPrefix, /// The box is taken out of the document flow and positioned in reference to the page viewport. fixed, + + pub fn parse(input: *css.Parser) css.Result(Position) { + const location = input.currentSourceLocation(); + const ident = switch (input.expectIdent()) { + .err => |e| return .{ .err = e }, + .result => |v| v, + }; + + const PositionKeyword = enum { + static, + relative, + absolute, + fixed, + sticky, + @"-webkit-sticky", + }; + + const keyword_map = bun.ComptimeStringMap(PositionKeyword, .{ + .{ "static", .static }, + .{ "relative", .relative }, + .{ "absolute", .absolute }, + .{ "fixed", .fixed }, + .{ "sticky", .sticky }, + .{ "-webkit-sticky", .@"-webkit-sticky" }, + }); + + const keyword = keyword_map.get(ident) orelse { + return .{ .err = location.newUnexpectedTokenError(.{ .ident = ident }) }; + }; + + return .{ .result = switch (keyword) { + .static => .static, + .relative => .relative, + .absolute => .absolute, + .fixed => .fixed, + .sticky => .{ .sticky = css.VendorPrefix{ .none = true } }, + .@"-webkit-sticky" => .{ .sticky = css.VendorPrefix{ .webkit = true } }, + } }; + } + + pub fn toCss(this: *const Position, comptime W: type, dest: *css.Printer(W)) css.PrintErr!void { + return switch (this.*) { + .static => dest.writeStr("static"), + .relative => dest.writeStr("relative"), + .absolute => dest.writeStr("absolute"), + .fixed => dest.writeStr("fixed"), + .sticky => |prefix| { + try prefix.toCss(W, dest); + return dest.writeStr("sticky"); + }, + }; + } + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } }; diff --git a/src/css/properties/properties_generated.zig b/src/css/properties/properties_generated.zig index f66f27f93f..d60dc7e3b4 100644 --- a/src/css/properties/properties_generated.zig +++ b/src/css/properties/properties_generated.zig @@ -25,7 +25,9 @@ const LengthPercentageOrAuto = css_values.length.LengthPercentageOrAuto; const PropertyCategory = css.PropertyCategory; const LogicalGroup = css.LogicalGroup; const CSSNumber = css.css_values.number.CSSNumber; +const CSSNumberFns = css.css_values.number.CSSNumberFns; const CSSInteger = css.css_values.number.CSSInteger; +const CSSIntegerFns = css.css_values.number.CSSIntegerFns; const NumberOrPercentage = css.css_values.percentage.NumberOrPercentage; const Percentage = css.css_values.percentage.Percentage; const Angle = css.css_values.angle.Angle; @@ -98,51 +100,51 @@ const BorderInlineStart = border.BorderInlineStart; const BorderInlineEnd = border.BorderInlineEnd; const BorderBlock = border.BorderBlock; const BorderInline = border.BorderInline; -// const Outline = outline.Outline; -// const OutlineStyle = outline.OutlineStyle; -// const FlexDirection = flex.FlexDirection; -// const FlexWrap = flex.FlexWrap; -// const FlexFlow = flex.FlexFlow; -// const Flex = flex.Flex; -// const BoxOrient = flex.BoxOrient; -// const BoxDirection = flex.BoxDirection; -// const BoxAlign = flex.BoxAlign; -// const BoxPack = flex.BoxPack; -// const BoxLines = flex.BoxLines; -// const FlexPack = flex.FlexPack; -// const FlexItemAlign = flex.FlexItemAlign; -// const FlexLinePack = flex.FlexLinePack; -// const AlignContent = @"align".AlignContent; -// const JustifyContent = @"align".JustifyContent; -// const PlaceContent = @"align".PlaceContent; -// const AlignSelf = @"align".AlignSelf; -// const JustifySelf = @"align".JustifySelf; -// const PlaceSelf = @"align".PlaceSelf; -// const AlignItems = @"align".AlignItems; -// const JustifyItems = @"align".JustifyItems; -// const PlaceItems = @"align".PlaceItems; -// const GapValue = @"align".GapValue; -// const Gap = @"align".Gap; -// const MarginBlock = margin_padding.MarginBlock; -// const Margin = margin_padding.Margin; -// const MarginInline = margin_padding.MarginInline; -// const PaddingBlock = margin_padding.PaddingBlock; -// const PaddingInline = margin_padding.PaddingInline; -// const Padding = margin_padding.Padding; -// const ScrollMarginBlock = margin_padding.ScrollMarginBlock; -// const ScrollMarginInline = margin_padding.ScrollMarginInline; -// const ScrollMargin = margin_padding.ScrollMargin; -// const ScrollPaddingBlock = margin_padding.ScrollPaddingBlock; -// const ScrollPaddingInline = margin_padding.ScrollPaddingInline; -// const ScrollPadding = margin_padding.ScrollPadding; -// const FontWeight = font.FontWeight; -// const FontSize = font.FontSize; -// const FontStretch = font.FontStretch; -// const FontFamily = font.FontFamily; -// const FontStyle = font.FontStyle; -// const FontVariantCaps = font.FontVariantCaps; -// const LineHeight = font.LineHeight; -// const Font = font.Font; +const Outline = outline.Outline; +const OutlineStyle = outline.OutlineStyle; +const FlexDirection = flex.FlexDirection; +const FlexWrap = flex.FlexWrap; +const FlexFlow = flex.FlexFlow; +const Flex = flex.Flex; +const BoxOrient = flex.BoxOrient; +const BoxDirection = flex.BoxDirection; +const BoxAlign = flex.BoxAlign; +const BoxPack = flex.BoxPack; +const BoxLines = flex.BoxLines; +const FlexPack = flex.FlexPack; +const FlexItemAlign = flex.FlexItemAlign; +const FlexLinePack = flex.FlexLinePack; +const AlignContent = @"align".AlignContent; +const JustifyContent = @"align".JustifyContent; +const PlaceContent = @"align".PlaceContent; +const AlignSelf = @"align".AlignSelf; +const JustifySelf = @"align".JustifySelf; +const PlaceSelf = @"align".PlaceSelf; +const AlignItems = @"align".AlignItems; +const JustifyItems = @"align".JustifyItems; +const PlaceItems = @"align".PlaceItems; +const GapValue = @"align".GapValue; +const Gap = @"align".Gap; +const MarginBlock = margin_padding.MarginBlock; +const Margin = margin_padding.Margin; +const MarginInline = margin_padding.MarginInline; +const PaddingBlock = margin_padding.PaddingBlock; +const PaddingInline = margin_padding.PaddingInline; +const Padding = margin_padding.Padding; +const ScrollMarginBlock = margin_padding.ScrollMarginBlock; +const ScrollMarginInline = margin_padding.ScrollMarginInline; +const ScrollMargin = margin_padding.ScrollMargin; +const ScrollPaddingBlock = margin_padding.ScrollPaddingBlock; +const ScrollPaddingInline = margin_padding.ScrollPaddingInline; +const ScrollPadding = margin_padding.ScrollPadding; +const FontWeight = font.FontWeight; +const FontSize = font.FontSize; +const FontStretch = font.FontStretch; +const FontFamily = font.FontFamily; +const FontStyle = font.FontStyle; +const FontVariantCaps = font.FontVariantCaps; +const LineHeight = font.LineHeight; +const Font = font.Font; // const VerticalAlign = font.VerticalAlign; // const Transition = transition.Transition; // const AnimationNameList = animation.AnimationNameList; @@ -187,7 +189,7 @@ const BorderInline = border.BorderInline; // const TextEmphasisPosition = text.TextEmphasisPosition; // const TextShadow = text.TextShadow; // const TextSizeAdjust = text.TextSizeAdjust; -// const Direction = text.Direction; +const Direction = text.Direction; // const UnicodeBidi = text.UnicodeBidi; // const BoxDecorationBreak = text.BoxDecorationBreak; // const Resize = ui.Resize; @@ -215,35 +217,80 @@ const Composes = css_modules.Composes; // const ShapeRendering = svg.ShapeRendering; // const TextRendering = svg.TextRendering; // const ImageRendering = svg.ImageRendering; -// const ClipPath = masking.ClipPath; -// const MaskMode = masking.MaskMode; -// const MaskClip = masking.MaskClip; -// const GeometryBox = masking.GeometryBox; -// const MaskComposite = masking.MaskComposite; -// const MaskType = masking.MaskType; -// const Mask = masking.Mask; -// const MaskBorderMode = masking.MaskBorderMode; -// const MaskBorder = masking.MaskBorder; -// const WebKitMaskComposite = masking.WebKitMaskComposite; -// const WebKitMaskSourceType = masking.WebKitMaskSourceType; -// const BackgroundRepeat = background.BackgroundRepeat; -// const BackgroundSize = background.BackgroundSize; +const ClipPath = masking.ClipPath; +const MaskMode = masking.MaskMode; +const MaskClip = masking.MaskClip; +const GeometryBox = masking.GeometryBox; +const MaskComposite = masking.MaskComposite; +const MaskType = masking.MaskType; +const Mask = masking.Mask; +const MaskBorderMode = masking.MaskBorderMode; +const MaskBorder = masking.MaskBorder; +const WebKitMaskComposite = masking.WebKitMaskComposite; +const WebKitMaskSourceType = masking.WebKitMaskSourceType; +const BackgroundRepeat = background.BackgroundRepeat; +const BackgroundSize = background.BackgroundSize; // const FilterList = effects.FilterList; // const ContainerType = contain.ContainerType; // const Container = contain.Container; // const ContainerNameList = contain.ContainerNameList; const CustomPropertyName = custom.CustomPropertyName; -// const display = css.css_properties.display; +const display = css.css_properties.display; const Position = position.Position; const Result = css.Result; +const BabyList = bun.BabyList; const ArrayList = std.ArrayListUnmanaged; const SmallList = css.SmallList; pub const Property = union(PropertyIdTag) { @"background-color": CssColor, + @"background-image": SmallList(Image, 1), + @"background-position-x": SmallList(css_values.position.HorizontalPosition, 1), + @"background-position-y": SmallList(css_values.position.HorizontalPosition, 1), + @"background-position": SmallList(background.BackgroundPosition, 1), + @"background-size": SmallList(background.BackgroundSize, 1), + @"background-repeat": SmallList(background.BackgroundSize, 1), + @"background-attachment": SmallList(background.BackgroundAttachment, 1), + @"background-clip": struct { SmallList(background.BackgroundAttachment, 1), VendorPrefix }, + @"background-origin": SmallList(background.BackgroundOrigin, 1), + background: SmallList(background.Background, 1), + @"box-shadow": struct { SmallList(box_shadow.BoxShadow, 1), VendorPrefix }, + opacity: css.css_values.alpha.AlphaValue, color: CssColor, + display: display.Display, + visibility: display.Visibility, + width: size.Size, + height: size.Size, + @"min-width": size.Size, + @"min-height": size.Size, + @"max-width": size.MaxSize, + @"max-height": size.MaxSize, + @"block-size": size.Size, + @"inline-size": size.Size, + @"min-block-size": size.Size, + @"min-inline-size": size.Size, + @"max-block-size": size.MaxSize, + @"max-inline-size": size.MaxSize, + @"box-sizing": struct { size.BoxSizing, VendorPrefix }, + @"aspect-ratio": size.AspectRatio, + overflow: overflow.Overflow, + @"overflow-x": overflow.OverflowKeyword, + @"overflow-y": overflow.OverflowKeyword, + @"text-overflow": struct { overflow.TextOverflow, VendorPrefix }, + position: position.Position, + top: LengthPercentageOrAuto, + bottom: LengthPercentageOrAuto, + left: LengthPercentageOrAuto, + right: LengthPercentageOrAuto, + @"inset-block-start": LengthPercentageOrAuto, + @"inset-block-end": LengthPercentageOrAuto, + @"inset-inline-start": LengthPercentageOrAuto, + @"inset-inline-end": LengthPercentageOrAuto, + @"inset-block": margin_padding.InsetBlock, + @"inset-inline": margin_padding.InsetInline, + inset: margin_padding.Inset, @"border-spacing": css.css_values.size.Size2D(Length), @"border-top-color": CssColor, @"border-bottom-color": CssColor, @@ -259,19 +306,3720 @@ pub const Property = union(PropertyIdTag) { @"border-right-style": border.LineStyle, @"border-block-start-style": border.LineStyle, @"border-block-end-style": border.LineStyle, + @"border-inline-start-style": border.LineStyle, + @"border-inline-end-style": border.LineStyle, @"border-top-width": BorderSideWidth, @"border-bottom-width": BorderSideWidth, @"border-left-width": BorderSideWidth, @"border-right-width": BorderSideWidth, + @"border-block-start-width": BorderSideWidth, + @"border-block-end-width": BorderSideWidth, + @"border-inline-start-width": BorderSideWidth, + @"border-inline-end-width": BorderSideWidth, + @"border-top-left-radius": struct { Size2D(LengthPercentage), VendorPrefix }, + @"border-top-right-radius": struct { Size2D(LengthPercentage), VendorPrefix }, + @"border-bottom-left-radius": struct { Size2D(LengthPercentage), VendorPrefix }, + @"border-bottom-right-radius": struct { Size2D(LengthPercentage), VendorPrefix }, + @"border-start-start-radius": Size2D(LengthPercentage), + @"border-start-end-radius": Size2D(LengthPercentage), + @"border-end-start-radius": Size2D(LengthPercentage), + @"border-end-end-radius": Size2D(LengthPercentage), + @"border-radius": struct { BorderRadius, VendorPrefix }, + @"border-image-source": Image, + @"border-image-outset": Rect(LengthOrNumber), + @"border-image-repeat": BorderImageRepeat, + @"border-image-width": Rect(BorderImageSideWidth), + @"border-image-slice": BorderImageSlice, + @"border-image": struct { BorderImage, VendorPrefix }, + @"border-color": BorderColor, + @"border-style": BorderStyle, + @"border-width": BorderWidth, + @"border-block-color": BorderBlockColor, + @"border-block-style": BorderBlockStyle, + @"border-block-width": BorderBlockWidth, + @"border-inline-color": BorderInlineColor, + @"border-inline-style": BorderInlineStyle, + @"border-inline-width": BorderInlineWidth, + border: Border, + @"border-top": BorderTop, + @"border-bottom": BorderBottom, + @"border-left": BorderLeft, + @"border-right": BorderRight, + @"border-block": BorderBlock, + @"border-block-start": BorderBlockStart, + @"border-block-end": BorderBlockEnd, + @"border-inline": BorderInline, + @"border-inline-start": BorderInlineStart, + @"border-inline-end": BorderInlineEnd, + outline: Outline, @"outline-color": CssColor, + @"outline-style": OutlineStyle, + @"outline-width": BorderSideWidth, + @"flex-direction": struct { FlexDirection, VendorPrefix }, + @"flex-wrap": struct { FlexWrap, VendorPrefix }, + @"flex-flow": struct { FlexFlow, VendorPrefix }, + @"flex-grow": struct { CSSNumber, VendorPrefix }, + @"flex-shrink": struct { CSSNumber, VendorPrefix }, + @"flex-basis": struct { LengthPercentageOrAuto, VendorPrefix }, + flex: struct { Flex, VendorPrefix }, + order: struct { CSSInteger, VendorPrefix }, + @"align-content": struct { AlignContent, VendorPrefix }, + @"justify-content": struct { JustifyContent, VendorPrefix }, + @"place-content": PlaceContent, + @"align-self": struct { AlignSelf, VendorPrefix }, + @"justify-self": JustifySelf, + @"place-self": PlaceSelf, + @"align-items": struct { AlignItems, VendorPrefix }, + @"justify-items": JustifyItems, + @"place-items": PlaceItems, + @"row-gap": GapValue, + @"column-gap": GapValue, + gap: Gap, + @"box-orient": struct { BoxOrient, VendorPrefix }, + @"box-direction": struct { BoxDirection, VendorPrefix }, + @"box-ordinal-group": struct { CSSInteger, VendorPrefix }, + @"box-align": struct { BoxAlign, VendorPrefix }, + @"box-flex": struct { CSSNumber, VendorPrefix }, + @"box-flex-group": struct { CSSInteger, VendorPrefix }, + @"box-pack": struct { BoxPack, VendorPrefix }, + @"box-lines": struct { BoxLines, VendorPrefix }, + @"flex-pack": struct { FlexPack, VendorPrefix }, + @"flex-order": struct { CSSInteger, VendorPrefix }, + @"flex-align": struct { BoxAlign, VendorPrefix }, + @"flex-item-align": struct { FlexItemAlign, VendorPrefix }, + @"flex-line-pack": struct { FlexLinePack, VendorPrefix }, + @"flex-positive": struct { CSSNumber, VendorPrefix }, + @"flex-negative": struct { CSSNumber, VendorPrefix }, + @"flex-preferred-size": struct { LengthPercentageOrAuto, VendorPrefix }, + @"margin-top": LengthPercentageOrAuto, + @"margin-bottom": LengthPercentageOrAuto, + @"margin-left": LengthPercentageOrAuto, + @"margin-right": LengthPercentageOrAuto, + @"margin-block-start": LengthPercentageOrAuto, + @"margin-block-end": LengthPercentageOrAuto, + @"margin-inline-start": LengthPercentageOrAuto, + @"margin-inline-end": LengthPercentageOrAuto, + @"margin-block": MarginBlock, + @"margin-inline": MarginInline, + margin: Margin, + @"padding-top": LengthPercentageOrAuto, + @"padding-bottom": LengthPercentageOrAuto, + @"padding-left": LengthPercentageOrAuto, + @"padding-right": LengthPercentageOrAuto, + @"padding-block-start": LengthPercentageOrAuto, + @"padding-block-end": LengthPercentageOrAuto, + @"padding-inline-start": LengthPercentageOrAuto, + @"padding-inline-end": LengthPercentageOrAuto, + @"padding-block": PaddingBlock, + @"padding-inline": PaddingInline, + padding: Padding, + @"scroll-margin-top": LengthPercentageOrAuto, + @"scroll-margin-bottom": LengthPercentageOrAuto, + @"scroll-margin-left": LengthPercentageOrAuto, + @"scroll-margin-right": LengthPercentageOrAuto, + @"scroll-margin-block-start": LengthPercentageOrAuto, + @"scroll-margin-block-end": LengthPercentageOrAuto, + @"scroll-margin-inline-start": LengthPercentageOrAuto, + @"scroll-margin-inline-end": LengthPercentageOrAuto, + @"scroll-margin-block": ScrollMarginBlock, + @"scroll-margin-inline": ScrollMarginInline, + @"scroll-margin": ScrollMargin, + @"scroll-padding-top": LengthPercentageOrAuto, + @"scroll-padding-bottom": LengthPercentageOrAuto, + @"scroll-padding-left": LengthPercentageOrAuto, + @"scroll-padding-right": LengthPercentageOrAuto, + @"scroll-padding-block-start": LengthPercentageOrAuto, + @"scroll-padding-block-end": LengthPercentageOrAuto, + @"scroll-padding-inline-start": LengthPercentageOrAuto, + @"scroll-padding-inline-end": LengthPercentageOrAuto, + @"scroll-padding-block": ScrollPaddingBlock, + @"scroll-padding-inline": ScrollPaddingInline, + @"scroll-padding": ScrollPadding, + @"font-weight": FontWeight, + @"font-size": FontSize, + @"font-stretch": FontStretch, + @"font-family": BabyList(FontFamily), + @"font-style": FontStyle, + @"font-variant-caps": FontVariantCaps, + @"line-height": LineHeight, + font: Font, @"text-decoration-color": struct { CssColor, VendorPrefix }, @"text-emphasis-color": struct { CssColor, VendorPrefix }, + direction: Direction, composes: Composes, + @"mask-image": struct { SmallList(Image, 1), VendorPrefix }, + @"mask-mode": SmallList(MaskMode, 1), + @"mask-repeat": struct { SmallList(BackgroundRepeat, 1), VendorPrefix }, + @"mask-position-x": SmallList(HorizontalPosition, 1), + @"mask-position-y": SmallList(VerticalPosition, 1), + @"mask-position": struct { SmallList(Position, 1), VendorPrefix }, + @"mask-clip": struct { SmallList(MaskClip, 1), VendorPrefix }, + @"mask-origin": struct { SmallList(GeometryBox, 1), VendorPrefix }, + @"mask-size": struct { SmallList(BackgroundSize, 1), VendorPrefix }, + @"mask-composite": SmallList(MaskComposite, 1), + @"mask-type": MaskType, + mask: struct { SmallList(Mask, 1), VendorPrefix }, + @"mask-border-source": Image, + @"mask-border-mode": MaskBorderMode, + @"mask-border-slice": BorderImageSlice, + @"mask-border-width": Rect(BorderImageSideWidth), + @"mask-border-outset": Rect(LengthOrNumber), + @"mask-border-repeat": BorderImageRepeat, + @"mask-border": MaskBorder, + @"-webkit-mask-composite": SmallList(WebKitMaskComposite, 1), + @"mask-source-type": struct { SmallList(WebKitMaskSourceType, 1), VendorPrefix }, + @"mask-box-image": struct { BorderImage, VendorPrefix }, + @"mask-box-image-source": struct { Image, VendorPrefix }, + @"mask-box-image-slice": struct { BorderImageSlice, VendorPrefix }, + @"mask-box-image-width": struct { Rect(BorderImageSideWidth), VendorPrefix }, + @"mask-box-image-outset": struct { Rect(LengthOrNumber), VendorPrefix }, + @"mask-box-image-repeat": struct { BorderImageRepeat, VendorPrefix }, all: CSSWideKeyword, unparsed: UnparsedProperty, custom: CustomProperty, pub usingnamespace PropertyImpl(); + + // Sanity check to make sure all types have the following functions: + // - deepClone() + // - eql() + // - parse() + // - toCss() + // + // We do this string concatenation thing so we get all the errors at once, + // instead of relying on Zig semantic analysis which usualy stops at the first error. + comptime { + const compile_error: []const u8 = compile_error: { + var compile_error: []const u8 = ""; + + if (!@hasDecl(CssColor, "deepClone")) { + compile_error = compile_error ++ @typeName(CssColor) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(CssColor, "parse")) { + compile_error = compile_error ++ @typeName(CssColor) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(CssColor, "toCss")) { + compile_error = compile_error ++ @typeName(CssColor) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(CssColor, "eql")) { + compile_error = compile_error ++ @typeName(CssColor) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(SmallList(Image, 1), "deepClone")) { + compile_error = compile_error ++ @typeName(SmallList(Image, 1)) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(SmallList(Image, 1), "parse")) { + compile_error = compile_error ++ @typeName(SmallList(Image, 1)) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(SmallList(Image, 1), "toCss")) { + compile_error = compile_error ++ @typeName(SmallList(Image, 1)) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(SmallList(Image, 1), "eql")) { + compile_error = compile_error ++ @typeName(SmallList(Image, 1)) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(SmallList(css_values.position.HorizontalPosition, 1), "deepClone")) { + compile_error = compile_error ++ @typeName(SmallList(css_values.position.HorizontalPosition, 1)) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(SmallList(css_values.position.HorizontalPosition, 1), "parse")) { + compile_error = compile_error ++ @typeName(SmallList(css_values.position.HorizontalPosition, 1)) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(SmallList(css_values.position.HorizontalPosition, 1), "toCss")) { + compile_error = compile_error ++ @typeName(SmallList(css_values.position.HorizontalPosition, 1)) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(SmallList(css_values.position.HorizontalPosition, 1), "eql")) { + compile_error = compile_error ++ @typeName(SmallList(css_values.position.HorizontalPosition, 1)) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(SmallList(css_values.position.HorizontalPosition, 1), "deepClone")) { + compile_error = compile_error ++ @typeName(SmallList(css_values.position.HorizontalPosition, 1)) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(SmallList(css_values.position.HorizontalPosition, 1), "parse")) { + compile_error = compile_error ++ @typeName(SmallList(css_values.position.HorizontalPosition, 1)) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(SmallList(css_values.position.HorizontalPosition, 1), "toCss")) { + compile_error = compile_error ++ @typeName(SmallList(css_values.position.HorizontalPosition, 1)) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(SmallList(css_values.position.HorizontalPosition, 1), "eql")) { + compile_error = compile_error ++ @typeName(SmallList(css_values.position.HorizontalPosition, 1)) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(SmallList(background.BackgroundPosition, 1), "deepClone")) { + compile_error = compile_error ++ @typeName(SmallList(background.BackgroundPosition, 1)) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(SmallList(background.BackgroundPosition, 1), "parse")) { + compile_error = compile_error ++ @typeName(SmallList(background.BackgroundPosition, 1)) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(SmallList(background.BackgroundPosition, 1), "toCss")) { + compile_error = compile_error ++ @typeName(SmallList(background.BackgroundPosition, 1)) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(SmallList(background.BackgroundPosition, 1), "eql")) { + compile_error = compile_error ++ @typeName(SmallList(background.BackgroundPosition, 1)) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(SmallList(background.BackgroundSize, 1), "deepClone")) { + compile_error = compile_error ++ @typeName(SmallList(background.BackgroundSize, 1)) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(SmallList(background.BackgroundSize, 1), "parse")) { + compile_error = compile_error ++ @typeName(SmallList(background.BackgroundSize, 1)) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(SmallList(background.BackgroundSize, 1), "toCss")) { + compile_error = compile_error ++ @typeName(SmallList(background.BackgroundSize, 1)) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(SmallList(background.BackgroundSize, 1), "eql")) { + compile_error = compile_error ++ @typeName(SmallList(background.BackgroundSize, 1)) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(SmallList(background.BackgroundSize, 1), "deepClone")) { + compile_error = compile_error ++ @typeName(SmallList(background.BackgroundSize, 1)) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(SmallList(background.BackgroundSize, 1), "parse")) { + compile_error = compile_error ++ @typeName(SmallList(background.BackgroundSize, 1)) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(SmallList(background.BackgroundSize, 1), "toCss")) { + compile_error = compile_error ++ @typeName(SmallList(background.BackgroundSize, 1)) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(SmallList(background.BackgroundSize, 1), "eql")) { + compile_error = compile_error ++ @typeName(SmallList(background.BackgroundSize, 1)) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(SmallList(background.BackgroundAttachment, 1), "deepClone")) { + compile_error = compile_error ++ @typeName(SmallList(background.BackgroundAttachment, 1)) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(SmallList(background.BackgroundAttachment, 1), "parse")) { + compile_error = compile_error ++ @typeName(SmallList(background.BackgroundAttachment, 1)) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(SmallList(background.BackgroundAttachment, 1), "toCss")) { + compile_error = compile_error ++ @typeName(SmallList(background.BackgroundAttachment, 1)) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(SmallList(background.BackgroundAttachment, 1), "eql")) { + compile_error = compile_error ++ @typeName(SmallList(background.BackgroundAttachment, 1)) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(SmallList(background.BackgroundAttachment, 1), "deepClone")) { + compile_error = compile_error ++ @typeName(SmallList(background.BackgroundAttachment, 1)) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(SmallList(background.BackgroundAttachment, 1), "parse")) { + compile_error = compile_error ++ @typeName(SmallList(background.BackgroundAttachment, 1)) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(SmallList(background.BackgroundAttachment, 1), "toCss")) { + compile_error = compile_error ++ @typeName(SmallList(background.BackgroundAttachment, 1)) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(SmallList(background.BackgroundAttachment, 1), "eql")) { + compile_error = compile_error ++ @typeName(SmallList(background.BackgroundAttachment, 1)) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(SmallList(background.BackgroundOrigin, 1), "deepClone")) { + compile_error = compile_error ++ @typeName(SmallList(background.BackgroundOrigin, 1)) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(SmallList(background.BackgroundOrigin, 1), "parse")) { + compile_error = compile_error ++ @typeName(SmallList(background.BackgroundOrigin, 1)) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(SmallList(background.BackgroundOrigin, 1), "toCss")) { + compile_error = compile_error ++ @typeName(SmallList(background.BackgroundOrigin, 1)) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(SmallList(background.BackgroundOrigin, 1), "eql")) { + compile_error = compile_error ++ @typeName(SmallList(background.BackgroundOrigin, 1)) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(SmallList(background.Background, 1), "deepClone")) { + compile_error = compile_error ++ @typeName(SmallList(background.Background, 1)) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(SmallList(background.Background, 1), "parse")) { + compile_error = compile_error ++ @typeName(SmallList(background.Background, 1)) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(SmallList(background.Background, 1), "toCss")) { + compile_error = compile_error ++ @typeName(SmallList(background.Background, 1)) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(SmallList(background.Background, 1), "eql")) { + compile_error = compile_error ++ @typeName(SmallList(background.Background, 1)) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(SmallList(box_shadow.BoxShadow, 1), "deepClone")) { + compile_error = compile_error ++ @typeName(SmallList(box_shadow.BoxShadow, 1)) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(SmallList(box_shadow.BoxShadow, 1), "parse")) { + compile_error = compile_error ++ @typeName(SmallList(box_shadow.BoxShadow, 1)) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(SmallList(box_shadow.BoxShadow, 1), "toCss")) { + compile_error = compile_error ++ @typeName(SmallList(box_shadow.BoxShadow, 1)) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(SmallList(box_shadow.BoxShadow, 1), "eql")) { + compile_error = compile_error ++ @typeName(SmallList(box_shadow.BoxShadow, 1)) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(css.css_values.alpha.AlphaValue, "deepClone")) { + compile_error = compile_error ++ @typeName(css.css_values.alpha.AlphaValue) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(css.css_values.alpha.AlphaValue, "parse")) { + compile_error = compile_error ++ @typeName(css.css_values.alpha.AlphaValue) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(css.css_values.alpha.AlphaValue, "toCss")) { + compile_error = compile_error ++ @typeName(css.css_values.alpha.AlphaValue) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(css.css_values.alpha.AlphaValue, "eql")) { + compile_error = compile_error ++ @typeName(css.css_values.alpha.AlphaValue) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(CssColor, "deepClone")) { + compile_error = compile_error ++ @typeName(CssColor) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(CssColor, "parse")) { + compile_error = compile_error ++ @typeName(CssColor) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(CssColor, "toCss")) { + compile_error = compile_error ++ @typeName(CssColor) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(CssColor, "eql")) { + compile_error = compile_error ++ @typeName(CssColor) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(display.Display, "deepClone")) { + compile_error = compile_error ++ @typeName(display.Display) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(display.Display, "parse")) { + compile_error = compile_error ++ @typeName(display.Display) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(display.Display, "toCss")) { + compile_error = compile_error ++ @typeName(display.Display) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(display.Display, "eql")) { + compile_error = compile_error ++ @typeName(display.Display) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(display.Visibility, "deepClone")) { + compile_error = compile_error ++ @typeName(display.Visibility) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(display.Visibility, "parse")) { + compile_error = compile_error ++ @typeName(display.Visibility) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(display.Visibility, "toCss")) { + compile_error = compile_error ++ @typeName(display.Visibility) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(display.Visibility, "eql")) { + compile_error = compile_error ++ @typeName(display.Visibility) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(size.Size, "deepClone")) { + compile_error = compile_error ++ @typeName(size.Size) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(size.Size, "parse")) { + compile_error = compile_error ++ @typeName(size.Size) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(size.Size, "toCss")) { + compile_error = compile_error ++ @typeName(size.Size) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(size.Size, "eql")) { + compile_error = compile_error ++ @typeName(size.Size) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(size.Size, "deepClone")) { + compile_error = compile_error ++ @typeName(size.Size) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(size.Size, "parse")) { + compile_error = compile_error ++ @typeName(size.Size) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(size.Size, "toCss")) { + compile_error = compile_error ++ @typeName(size.Size) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(size.Size, "eql")) { + compile_error = compile_error ++ @typeName(size.Size) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(size.Size, "deepClone")) { + compile_error = compile_error ++ @typeName(size.Size) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(size.Size, "parse")) { + compile_error = compile_error ++ @typeName(size.Size) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(size.Size, "toCss")) { + compile_error = compile_error ++ @typeName(size.Size) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(size.Size, "eql")) { + compile_error = compile_error ++ @typeName(size.Size) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(size.Size, "deepClone")) { + compile_error = compile_error ++ @typeName(size.Size) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(size.Size, "parse")) { + compile_error = compile_error ++ @typeName(size.Size) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(size.Size, "toCss")) { + compile_error = compile_error ++ @typeName(size.Size) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(size.Size, "eql")) { + compile_error = compile_error ++ @typeName(size.Size) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(size.MaxSize, "deepClone")) { + compile_error = compile_error ++ @typeName(size.MaxSize) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(size.MaxSize, "parse")) { + compile_error = compile_error ++ @typeName(size.MaxSize) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(size.MaxSize, "toCss")) { + compile_error = compile_error ++ @typeName(size.MaxSize) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(size.MaxSize, "eql")) { + compile_error = compile_error ++ @typeName(size.MaxSize) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(size.MaxSize, "deepClone")) { + compile_error = compile_error ++ @typeName(size.MaxSize) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(size.MaxSize, "parse")) { + compile_error = compile_error ++ @typeName(size.MaxSize) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(size.MaxSize, "toCss")) { + compile_error = compile_error ++ @typeName(size.MaxSize) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(size.MaxSize, "eql")) { + compile_error = compile_error ++ @typeName(size.MaxSize) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(size.Size, "deepClone")) { + compile_error = compile_error ++ @typeName(size.Size) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(size.Size, "parse")) { + compile_error = compile_error ++ @typeName(size.Size) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(size.Size, "toCss")) { + compile_error = compile_error ++ @typeName(size.Size) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(size.Size, "eql")) { + compile_error = compile_error ++ @typeName(size.Size) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(size.Size, "deepClone")) { + compile_error = compile_error ++ @typeName(size.Size) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(size.Size, "parse")) { + compile_error = compile_error ++ @typeName(size.Size) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(size.Size, "toCss")) { + compile_error = compile_error ++ @typeName(size.Size) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(size.Size, "eql")) { + compile_error = compile_error ++ @typeName(size.Size) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(size.Size, "deepClone")) { + compile_error = compile_error ++ @typeName(size.Size) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(size.Size, "parse")) { + compile_error = compile_error ++ @typeName(size.Size) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(size.Size, "toCss")) { + compile_error = compile_error ++ @typeName(size.Size) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(size.Size, "eql")) { + compile_error = compile_error ++ @typeName(size.Size) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(size.Size, "deepClone")) { + compile_error = compile_error ++ @typeName(size.Size) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(size.Size, "parse")) { + compile_error = compile_error ++ @typeName(size.Size) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(size.Size, "toCss")) { + compile_error = compile_error ++ @typeName(size.Size) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(size.Size, "eql")) { + compile_error = compile_error ++ @typeName(size.Size) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(size.MaxSize, "deepClone")) { + compile_error = compile_error ++ @typeName(size.MaxSize) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(size.MaxSize, "parse")) { + compile_error = compile_error ++ @typeName(size.MaxSize) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(size.MaxSize, "toCss")) { + compile_error = compile_error ++ @typeName(size.MaxSize) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(size.MaxSize, "eql")) { + compile_error = compile_error ++ @typeName(size.MaxSize) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(size.MaxSize, "deepClone")) { + compile_error = compile_error ++ @typeName(size.MaxSize) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(size.MaxSize, "parse")) { + compile_error = compile_error ++ @typeName(size.MaxSize) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(size.MaxSize, "toCss")) { + compile_error = compile_error ++ @typeName(size.MaxSize) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(size.MaxSize, "eql")) { + compile_error = compile_error ++ @typeName(size.MaxSize) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(size.BoxSizing, "deepClone")) { + compile_error = compile_error ++ @typeName(size.BoxSizing) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(size.BoxSizing, "parse")) { + compile_error = compile_error ++ @typeName(size.BoxSizing) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(size.BoxSizing, "toCss")) { + compile_error = compile_error ++ @typeName(size.BoxSizing) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(size.BoxSizing, "eql")) { + compile_error = compile_error ++ @typeName(size.BoxSizing) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(size.AspectRatio, "deepClone")) { + compile_error = compile_error ++ @typeName(size.AspectRatio) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(size.AspectRatio, "parse")) { + compile_error = compile_error ++ @typeName(size.AspectRatio) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(size.AspectRatio, "toCss")) { + compile_error = compile_error ++ @typeName(size.AspectRatio) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(size.AspectRatio, "eql")) { + compile_error = compile_error ++ @typeName(size.AspectRatio) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(overflow.Overflow, "deepClone")) { + compile_error = compile_error ++ @typeName(overflow.Overflow) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(overflow.Overflow, "parse")) { + compile_error = compile_error ++ @typeName(overflow.Overflow) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(overflow.Overflow, "toCss")) { + compile_error = compile_error ++ @typeName(overflow.Overflow) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(overflow.Overflow, "eql")) { + compile_error = compile_error ++ @typeName(overflow.Overflow) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(overflow.OverflowKeyword, "deepClone")) { + compile_error = compile_error ++ @typeName(overflow.OverflowKeyword) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(overflow.OverflowKeyword, "parse")) { + compile_error = compile_error ++ @typeName(overflow.OverflowKeyword) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(overflow.OverflowKeyword, "toCss")) { + compile_error = compile_error ++ @typeName(overflow.OverflowKeyword) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(overflow.OverflowKeyword, "eql")) { + compile_error = compile_error ++ @typeName(overflow.OverflowKeyword) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(overflow.OverflowKeyword, "deepClone")) { + compile_error = compile_error ++ @typeName(overflow.OverflowKeyword) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(overflow.OverflowKeyword, "parse")) { + compile_error = compile_error ++ @typeName(overflow.OverflowKeyword) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(overflow.OverflowKeyword, "toCss")) { + compile_error = compile_error ++ @typeName(overflow.OverflowKeyword) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(overflow.OverflowKeyword, "eql")) { + compile_error = compile_error ++ @typeName(overflow.OverflowKeyword) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(overflow.TextOverflow, "deepClone")) { + compile_error = compile_error ++ @typeName(overflow.TextOverflow) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(overflow.TextOverflow, "parse")) { + compile_error = compile_error ++ @typeName(overflow.TextOverflow) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(overflow.TextOverflow, "toCss")) { + compile_error = compile_error ++ @typeName(overflow.TextOverflow) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(overflow.TextOverflow, "eql")) { + compile_error = compile_error ++ @typeName(overflow.TextOverflow) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(position.Position, "deepClone")) { + compile_error = compile_error ++ @typeName(position.Position) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(position.Position, "parse")) { + compile_error = compile_error ++ @typeName(position.Position) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(position.Position, "toCss")) { + compile_error = compile_error ++ @typeName(position.Position) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(position.Position, "eql")) { + compile_error = compile_error ++ @typeName(position.Position) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "deepClone")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "parse")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "toCss")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "eql")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "deepClone")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "parse")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "toCss")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "eql")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "deepClone")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "parse")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "toCss")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "eql")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "deepClone")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "parse")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "toCss")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "eql")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "deepClone")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "parse")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "toCss")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "eql")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "deepClone")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "parse")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "toCss")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "eql")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "deepClone")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "parse")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "toCss")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "eql")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "deepClone")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "parse")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "toCss")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "eql")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(margin_padding.InsetBlock, "deepClone")) { + compile_error = compile_error ++ @typeName(margin_padding.InsetBlock) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(margin_padding.InsetBlock, "parse")) { + compile_error = compile_error ++ @typeName(margin_padding.InsetBlock) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(margin_padding.InsetBlock, "toCss")) { + compile_error = compile_error ++ @typeName(margin_padding.InsetBlock) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(margin_padding.InsetBlock, "eql")) { + compile_error = compile_error ++ @typeName(margin_padding.InsetBlock) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(margin_padding.InsetInline, "deepClone")) { + compile_error = compile_error ++ @typeName(margin_padding.InsetInline) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(margin_padding.InsetInline, "parse")) { + compile_error = compile_error ++ @typeName(margin_padding.InsetInline) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(margin_padding.InsetInline, "toCss")) { + compile_error = compile_error ++ @typeName(margin_padding.InsetInline) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(margin_padding.InsetInline, "eql")) { + compile_error = compile_error ++ @typeName(margin_padding.InsetInline) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(margin_padding.Inset, "deepClone")) { + compile_error = compile_error ++ @typeName(margin_padding.Inset) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(margin_padding.Inset, "parse")) { + compile_error = compile_error ++ @typeName(margin_padding.Inset) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(margin_padding.Inset, "toCss")) { + compile_error = compile_error ++ @typeName(margin_padding.Inset) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(margin_padding.Inset, "eql")) { + compile_error = compile_error ++ @typeName(margin_padding.Inset) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(css.css_values.size.Size2D(Length), "deepClone")) { + compile_error = compile_error ++ @typeName(css.css_values.size.Size2D(Length)) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(css.css_values.size.Size2D(Length), "parse")) { + compile_error = compile_error ++ @typeName(css.css_values.size.Size2D(Length)) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(css.css_values.size.Size2D(Length), "toCss")) { + compile_error = compile_error ++ @typeName(css.css_values.size.Size2D(Length)) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(css.css_values.size.Size2D(Length), "eql")) { + compile_error = compile_error ++ @typeName(css.css_values.size.Size2D(Length)) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(CssColor, "deepClone")) { + compile_error = compile_error ++ @typeName(CssColor) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(CssColor, "parse")) { + compile_error = compile_error ++ @typeName(CssColor) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(CssColor, "toCss")) { + compile_error = compile_error ++ @typeName(CssColor) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(CssColor, "eql")) { + compile_error = compile_error ++ @typeName(CssColor) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(CssColor, "deepClone")) { + compile_error = compile_error ++ @typeName(CssColor) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(CssColor, "parse")) { + compile_error = compile_error ++ @typeName(CssColor) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(CssColor, "toCss")) { + compile_error = compile_error ++ @typeName(CssColor) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(CssColor, "eql")) { + compile_error = compile_error ++ @typeName(CssColor) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(CssColor, "deepClone")) { + compile_error = compile_error ++ @typeName(CssColor) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(CssColor, "parse")) { + compile_error = compile_error ++ @typeName(CssColor) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(CssColor, "toCss")) { + compile_error = compile_error ++ @typeName(CssColor) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(CssColor, "eql")) { + compile_error = compile_error ++ @typeName(CssColor) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(CssColor, "deepClone")) { + compile_error = compile_error ++ @typeName(CssColor) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(CssColor, "parse")) { + compile_error = compile_error ++ @typeName(CssColor) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(CssColor, "toCss")) { + compile_error = compile_error ++ @typeName(CssColor) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(CssColor, "eql")) { + compile_error = compile_error ++ @typeName(CssColor) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(CssColor, "deepClone")) { + compile_error = compile_error ++ @typeName(CssColor) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(CssColor, "parse")) { + compile_error = compile_error ++ @typeName(CssColor) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(CssColor, "toCss")) { + compile_error = compile_error ++ @typeName(CssColor) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(CssColor, "eql")) { + compile_error = compile_error ++ @typeName(CssColor) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(CssColor, "deepClone")) { + compile_error = compile_error ++ @typeName(CssColor) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(CssColor, "parse")) { + compile_error = compile_error ++ @typeName(CssColor) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(CssColor, "toCss")) { + compile_error = compile_error ++ @typeName(CssColor) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(CssColor, "eql")) { + compile_error = compile_error ++ @typeName(CssColor) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(CssColor, "deepClone")) { + compile_error = compile_error ++ @typeName(CssColor) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(CssColor, "parse")) { + compile_error = compile_error ++ @typeName(CssColor) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(CssColor, "toCss")) { + compile_error = compile_error ++ @typeName(CssColor) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(CssColor, "eql")) { + compile_error = compile_error ++ @typeName(CssColor) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(CssColor, "deepClone")) { + compile_error = compile_error ++ @typeName(CssColor) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(CssColor, "parse")) { + compile_error = compile_error ++ @typeName(CssColor) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(CssColor, "toCss")) { + compile_error = compile_error ++ @typeName(CssColor) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(CssColor, "eql")) { + compile_error = compile_error ++ @typeName(CssColor) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(border.LineStyle, "deepClone")) { + compile_error = compile_error ++ @typeName(border.LineStyle) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(border.LineStyle, "parse")) { + compile_error = compile_error ++ @typeName(border.LineStyle) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(border.LineStyle, "toCss")) { + compile_error = compile_error ++ @typeName(border.LineStyle) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(border.LineStyle, "eql")) { + compile_error = compile_error ++ @typeName(border.LineStyle) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(border.LineStyle, "deepClone")) { + compile_error = compile_error ++ @typeName(border.LineStyle) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(border.LineStyle, "parse")) { + compile_error = compile_error ++ @typeName(border.LineStyle) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(border.LineStyle, "toCss")) { + compile_error = compile_error ++ @typeName(border.LineStyle) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(border.LineStyle, "eql")) { + compile_error = compile_error ++ @typeName(border.LineStyle) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(border.LineStyle, "deepClone")) { + compile_error = compile_error ++ @typeName(border.LineStyle) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(border.LineStyle, "parse")) { + compile_error = compile_error ++ @typeName(border.LineStyle) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(border.LineStyle, "toCss")) { + compile_error = compile_error ++ @typeName(border.LineStyle) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(border.LineStyle, "eql")) { + compile_error = compile_error ++ @typeName(border.LineStyle) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(border.LineStyle, "deepClone")) { + compile_error = compile_error ++ @typeName(border.LineStyle) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(border.LineStyle, "parse")) { + compile_error = compile_error ++ @typeName(border.LineStyle) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(border.LineStyle, "toCss")) { + compile_error = compile_error ++ @typeName(border.LineStyle) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(border.LineStyle, "eql")) { + compile_error = compile_error ++ @typeName(border.LineStyle) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(border.LineStyle, "deepClone")) { + compile_error = compile_error ++ @typeName(border.LineStyle) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(border.LineStyle, "parse")) { + compile_error = compile_error ++ @typeName(border.LineStyle) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(border.LineStyle, "toCss")) { + compile_error = compile_error ++ @typeName(border.LineStyle) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(border.LineStyle, "eql")) { + compile_error = compile_error ++ @typeName(border.LineStyle) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(border.LineStyle, "deepClone")) { + compile_error = compile_error ++ @typeName(border.LineStyle) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(border.LineStyle, "parse")) { + compile_error = compile_error ++ @typeName(border.LineStyle) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(border.LineStyle, "toCss")) { + compile_error = compile_error ++ @typeName(border.LineStyle) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(border.LineStyle, "eql")) { + compile_error = compile_error ++ @typeName(border.LineStyle) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(border.LineStyle, "deepClone")) { + compile_error = compile_error ++ @typeName(border.LineStyle) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(border.LineStyle, "parse")) { + compile_error = compile_error ++ @typeName(border.LineStyle) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(border.LineStyle, "toCss")) { + compile_error = compile_error ++ @typeName(border.LineStyle) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(border.LineStyle, "eql")) { + compile_error = compile_error ++ @typeName(border.LineStyle) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(border.LineStyle, "deepClone")) { + compile_error = compile_error ++ @typeName(border.LineStyle) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(border.LineStyle, "parse")) { + compile_error = compile_error ++ @typeName(border.LineStyle) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(border.LineStyle, "toCss")) { + compile_error = compile_error ++ @typeName(border.LineStyle) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(border.LineStyle, "eql")) { + compile_error = compile_error ++ @typeName(border.LineStyle) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(BorderSideWidth, "deepClone")) { + compile_error = compile_error ++ @typeName(BorderSideWidth) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(BorderSideWidth, "parse")) { + compile_error = compile_error ++ @typeName(BorderSideWidth) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(BorderSideWidth, "toCss")) { + compile_error = compile_error ++ @typeName(BorderSideWidth) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(BorderSideWidth, "eql")) { + compile_error = compile_error ++ @typeName(BorderSideWidth) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(BorderSideWidth, "deepClone")) { + compile_error = compile_error ++ @typeName(BorderSideWidth) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(BorderSideWidth, "parse")) { + compile_error = compile_error ++ @typeName(BorderSideWidth) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(BorderSideWidth, "toCss")) { + compile_error = compile_error ++ @typeName(BorderSideWidth) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(BorderSideWidth, "eql")) { + compile_error = compile_error ++ @typeName(BorderSideWidth) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(BorderSideWidth, "deepClone")) { + compile_error = compile_error ++ @typeName(BorderSideWidth) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(BorderSideWidth, "parse")) { + compile_error = compile_error ++ @typeName(BorderSideWidth) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(BorderSideWidth, "toCss")) { + compile_error = compile_error ++ @typeName(BorderSideWidth) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(BorderSideWidth, "eql")) { + compile_error = compile_error ++ @typeName(BorderSideWidth) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(BorderSideWidth, "deepClone")) { + compile_error = compile_error ++ @typeName(BorderSideWidth) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(BorderSideWidth, "parse")) { + compile_error = compile_error ++ @typeName(BorderSideWidth) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(BorderSideWidth, "toCss")) { + compile_error = compile_error ++ @typeName(BorderSideWidth) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(BorderSideWidth, "eql")) { + compile_error = compile_error ++ @typeName(BorderSideWidth) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(BorderSideWidth, "deepClone")) { + compile_error = compile_error ++ @typeName(BorderSideWidth) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(BorderSideWidth, "parse")) { + compile_error = compile_error ++ @typeName(BorderSideWidth) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(BorderSideWidth, "toCss")) { + compile_error = compile_error ++ @typeName(BorderSideWidth) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(BorderSideWidth, "eql")) { + compile_error = compile_error ++ @typeName(BorderSideWidth) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(BorderSideWidth, "deepClone")) { + compile_error = compile_error ++ @typeName(BorderSideWidth) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(BorderSideWidth, "parse")) { + compile_error = compile_error ++ @typeName(BorderSideWidth) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(BorderSideWidth, "toCss")) { + compile_error = compile_error ++ @typeName(BorderSideWidth) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(BorderSideWidth, "eql")) { + compile_error = compile_error ++ @typeName(BorderSideWidth) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(BorderSideWidth, "deepClone")) { + compile_error = compile_error ++ @typeName(BorderSideWidth) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(BorderSideWidth, "parse")) { + compile_error = compile_error ++ @typeName(BorderSideWidth) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(BorderSideWidth, "toCss")) { + compile_error = compile_error ++ @typeName(BorderSideWidth) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(BorderSideWidth, "eql")) { + compile_error = compile_error ++ @typeName(BorderSideWidth) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(BorderSideWidth, "deepClone")) { + compile_error = compile_error ++ @typeName(BorderSideWidth) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(BorderSideWidth, "parse")) { + compile_error = compile_error ++ @typeName(BorderSideWidth) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(BorderSideWidth, "toCss")) { + compile_error = compile_error ++ @typeName(BorderSideWidth) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(BorderSideWidth, "eql")) { + compile_error = compile_error ++ @typeName(BorderSideWidth) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(Size2D(LengthPercentage), "deepClone")) { + compile_error = compile_error ++ @typeName(Size2D(LengthPercentage)) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(Size2D(LengthPercentage), "parse")) { + compile_error = compile_error ++ @typeName(Size2D(LengthPercentage)) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(Size2D(LengthPercentage), "toCss")) { + compile_error = compile_error ++ @typeName(Size2D(LengthPercentage)) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(Size2D(LengthPercentage), "eql")) { + compile_error = compile_error ++ @typeName(Size2D(LengthPercentage)) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(Size2D(LengthPercentage), "deepClone")) { + compile_error = compile_error ++ @typeName(Size2D(LengthPercentage)) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(Size2D(LengthPercentage), "parse")) { + compile_error = compile_error ++ @typeName(Size2D(LengthPercentage)) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(Size2D(LengthPercentage), "toCss")) { + compile_error = compile_error ++ @typeName(Size2D(LengthPercentage)) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(Size2D(LengthPercentage), "eql")) { + compile_error = compile_error ++ @typeName(Size2D(LengthPercentage)) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(Size2D(LengthPercentage), "deepClone")) { + compile_error = compile_error ++ @typeName(Size2D(LengthPercentage)) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(Size2D(LengthPercentage), "parse")) { + compile_error = compile_error ++ @typeName(Size2D(LengthPercentage)) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(Size2D(LengthPercentage), "toCss")) { + compile_error = compile_error ++ @typeName(Size2D(LengthPercentage)) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(Size2D(LengthPercentage), "eql")) { + compile_error = compile_error ++ @typeName(Size2D(LengthPercentage)) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(Size2D(LengthPercentage), "deepClone")) { + compile_error = compile_error ++ @typeName(Size2D(LengthPercentage)) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(Size2D(LengthPercentage), "parse")) { + compile_error = compile_error ++ @typeName(Size2D(LengthPercentage)) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(Size2D(LengthPercentage), "toCss")) { + compile_error = compile_error ++ @typeName(Size2D(LengthPercentage)) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(Size2D(LengthPercentage), "eql")) { + compile_error = compile_error ++ @typeName(Size2D(LengthPercentage)) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(Size2D(LengthPercentage), "deepClone")) { + compile_error = compile_error ++ @typeName(Size2D(LengthPercentage)) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(Size2D(LengthPercentage), "parse")) { + compile_error = compile_error ++ @typeName(Size2D(LengthPercentage)) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(Size2D(LengthPercentage), "toCss")) { + compile_error = compile_error ++ @typeName(Size2D(LengthPercentage)) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(Size2D(LengthPercentage), "eql")) { + compile_error = compile_error ++ @typeName(Size2D(LengthPercentage)) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(Size2D(LengthPercentage), "deepClone")) { + compile_error = compile_error ++ @typeName(Size2D(LengthPercentage)) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(Size2D(LengthPercentage), "parse")) { + compile_error = compile_error ++ @typeName(Size2D(LengthPercentage)) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(Size2D(LengthPercentage), "toCss")) { + compile_error = compile_error ++ @typeName(Size2D(LengthPercentage)) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(Size2D(LengthPercentage), "eql")) { + compile_error = compile_error ++ @typeName(Size2D(LengthPercentage)) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(Size2D(LengthPercentage), "deepClone")) { + compile_error = compile_error ++ @typeName(Size2D(LengthPercentage)) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(Size2D(LengthPercentage), "parse")) { + compile_error = compile_error ++ @typeName(Size2D(LengthPercentage)) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(Size2D(LengthPercentage), "toCss")) { + compile_error = compile_error ++ @typeName(Size2D(LengthPercentage)) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(Size2D(LengthPercentage), "eql")) { + compile_error = compile_error ++ @typeName(Size2D(LengthPercentage)) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(Size2D(LengthPercentage), "deepClone")) { + compile_error = compile_error ++ @typeName(Size2D(LengthPercentage)) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(Size2D(LengthPercentage), "parse")) { + compile_error = compile_error ++ @typeName(Size2D(LengthPercentage)) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(Size2D(LengthPercentage), "toCss")) { + compile_error = compile_error ++ @typeName(Size2D(LengthPercentage)) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(Size2D(LengthPercentage), "eql")) { + compile_error = compile_error ++ @typeName(Size2D(LengthPercentage)) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(BorderRadius, "deepClone")) { + compile_error = compile_error ++ @typeName(BorderRadius) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(BorderRadius, "parse")) { + compile_error = compile_error ++ @typeName(BorderRadius) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(BorderRadius, "toCss")) { + compile_error = compile_error ++ @typeName(BorderRadius) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(BorderRadius, "eql")) { + compile_error = compile_error ++ @typeName(BorderRadius) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(Image, "deepClone")) { + compile_error = compile_error ++ @typeName(Image) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(Image, "parse")) { + compile_error = compile_error ++ @typeName(Image) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(Image, "toCss")) { + compile_error = compile_error ++ @typeName(Image) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(Image, "eql")) { + compile_error = compile_error ++ @typeName(Image) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(Rect(LengthOrNumber), "deepClone")) { + compile_error = compile_error ++ @typeName(Rect(LengthOrNumber)) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(Rect(LengthOrNumber), "parse")) { + compile_error = compile_error ++ @typeName(Rect(LengthOrNumber)) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(Rect(LengthOrNumber), "toCss")) { + compile_error = compile_error ++ @typeName(Rect(LengthOrNumber)) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(Rect(LengthOrNumber), "eql")) { + compile_error = compile_error ++ @typeName(Rect(LengthOrNumber)) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(BorderImageRepeat, "deepClone")) { + compile_error = compile_error ++ @typeName(BorderImageRepeat) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(BorderImageRepeat, "parse")) { + compile_error = compile_error ++ @typeName(BorderImageRepeat) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(BorderImageRepeat, "toCss")) { + compile_error = compile_error ++ @typeName(BorderImageRepeat) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(BorderImageRepeat, "eql")) { + compile_error = compile_error ++ @typeName(BorderImageRepeat) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(Rect(BorderImageSideWidth), "deepClone")) { + compile_error = compile_error ++ @typeName(Rect(BorderImageSideWidth)) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(Rect(BorderImageSideWidth), "parse")) { + compile_error = compile_error ++ @typeName(Rect(BorderImageSideWidth)) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(Rect(BorderImageSideWidth), "toCss")) { + compile_error = compile_error ++ @typeName(Rect(BorderImageSideWidth)) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(Rect(BorderImageSideWidth), "eql")) { + compile_error = compile_error ++ @typeName(Rect(BorderImageSideWidth)) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(BorderImageSlice, "deepClone")) { + compile_error = compile_error ++ @typeName(BorderImageSlice) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(BorderImageSlice, "parse")) { + compile_error = compile_error ++ @typeName(BorderImageSlice) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(BorderImageSlice, "toCss")) { + compile_error = compile_error ++ @typeName(BorderImageSlice) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(BorderImageSlice, "eql")) { + compile_error = compile_error ++ @typeName(BorderImageSlice) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(BorderImage, "deepClone")) { + compile_error = compile_error ++ @typeName(BorderImage) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(BorderImage, "parse")) { + compile_error = compile_error ++ @typeName(BorderImage) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(BorderImage, "toCss")) { + compile_error = compile_error ++ @typeName(BorderImage) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(BorderImage, "eql")) { + compile_error = compile_error ++ @typeName(BorderImage) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(BorderColor, "deepClone")) { + compile_error = compile_error ++ @typeName(BorderColor) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(BorderColor, "parse")) { + compile_error = compile_error ++ @typeName(BorderColor) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(BorderColor, "toCss")) { + compile_error = compile_error ++ @typeName(BorderColor) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(BorderColor, "eql")) { + compile_error = compile_error ++ @typeName(BorderColor) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(BorderStyle, "deepClone")) { + compile_error = compile_error ++ @typeName(BorderStyle) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(BorderStyle, "parse")) { + compile_error = compile_error ++ @typeName(BorderStyle) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(BorderStyle, "toCss")) { + compile_error = compile_error ++ @typeName(BorderStyle) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(BorderStyle, "eql")) { + compile_error = compile_error ++ @typeName(BorderStyle) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(BorderWidth, "deepClone")) { + compile_error = compile_error ++ @typeName(BorderWidth) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(BorderWidth, "parse")) { + compile_error = compile_error ++ @typeName(BorderWidth) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(BorderWidth, "toCss")) { + compile_error = compile_error ++ @typeName(BorderWidth) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(BorderWidth, "eql")) { + compile_error = compile_error ++ @typeName(BorderWidth) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(BorderBlockColor, "deepClone")) { + compile_error = compile_error ++ @typeName(BorderBlockColor) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(BorderBlockColor, "parse")) { + compile_error = compile_error ++ @typeName(BorderBlockColor) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(BorderBlockColor, "toCss")) { + compile_error = compile_error ++ @typeName(BorderBlockColor) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(BorderBlockColor, "eql")) { + compile_error = compile_error ++ @typeName(BorderBlockColor) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(BorderBlockStyle, "deepClone")) { + compile_error = compile_error ++ @typeName(BorderBlockStyle) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(BorderBlockStyle, "parse")) { + compile_error = compile_error ++ @typeName(BorderBlockStyle) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(BorderBlockStyle, "toCss")) { + compile_error = compile_error ++ @typeName(BorderBlockStyle) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(BorderBlockStyle, "eql")) { + compile_error = compile_error ++ @typeName(BorderBlockStyle) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(BorderBlockWidth, "deepClone")) { + compile_error = compile_error ++ @typeName(BorderBlockWidth) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(BorderBlockWidth, "parse")) { + compile_error = compile_error ++ @typeName(BorderBlockWidth) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(BorderBlockWidth, "toCss")) { + compile_error = compile_error ++ @typeName(BorderBlockWidth) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(BorderBlockWidth, "eql")) { + compile_error = compile_error ++ @typeName(BorderBlockWidth) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(BorderInlineColor, "deepClone")) { + compile_error = compile_error ++ @typeName(BorderInlineColor) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(BorderInlineColor, "parse")) { + compile_error = compile_error ++ @typeName(BorderInlineColor) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(BorderInlineColor, "toCss")) { + compile_error = compile_error ++ @typeName(BorderInlineColor) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(BorderInlineColor, "eql")) { + compile_error = compile_error ++ @typeName(BorderInlineColor) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(BorderInlineStyle, "deepClone")) { + compile_error = compile_error ++ @typeName(BorderInlineStyle) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(BorderInlineStyle, "parse")) { + compile_error = compile_error ++ @typeName(BorderInlineStyle) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(BorderInlineStyle, "toCss")) { + compile_error = compile_error ++ @typeName(BorderInlineStyle) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(BorderInlineStyle, "eql")) { + compile_error = compile_error ++ @typeName(BorderInlineStyle) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(BorderInlineWidth, "deepClone")) { + compile_error = compile_error ++ @typeName(BorderInlineWidth) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(BorderInlineWidth, "parse")) { + compile_error = compile_error ++ @typeName(BorderInlineWidth) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(BorderInlineWidth, "toCss")) { + compile_error = compile_error ++ @typeName(BorderInlineWidth) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(BorderInlineWidth, "eql")) { + compile_error = compile_error ++ @typeName(BorderInlineWidth) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(Border, "deepClone")) { + compile_error = compile_error ++ @typeName(Border) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(Border, "parse")) { + compile_error = compile_error ++ @typeName(Border) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(Border, "toCss")) { + compile_error = compile_error ++ @typeName(Border) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(Border, "eql")) { + compile_error = compile_error ++ @typeName(Border) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(BorderTop, "deepClone")) { + compile_error = compile_error ++ @typeName(BorderTop) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(BorderTop, "parse")) { + compile_error = compile_error ++ @typeName(BorderTop) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(BorderTop, "toCss")) { + compile_error = compile_error ++ @typeName(BorderTop) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(BorderTop, "eql")) { + compile_error = compile_error ++ @typeName(BorderTop) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(BorderBottom, "deepClone")) { + compile_error = compile_error ++ @typeName(BorderBottom) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(BorderBottom, "parse")) { + compile_error = compile_error ++ @typeName(BorderBottom) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(BorderBottom, "toCss")) { + compile_error = compile_error ++ @typeName(BorderBottom) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(BorderBottom, "eql")) { + compile_error = compile_error ++ @typeName(BorderBottom) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(BorderLeft, "deepClone")) { + compile_error = compile_error ++ @typeName(BorderLeft) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(BorderLeft, "parse")) { + compile_error = compile_error ++ @typeName(BorderLeft) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(BorderLeft, "toCss")) { + compile_error = compile_error ++ @typeName(BorderLeft) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(BorderLeft, "eql")) { + compile_error = compile_error ++ @typeName(BorderLeft) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(BorderRight, "deepClone")) { + compile_error = compile_error ++ @typeName(BorderRight) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(BorderRight, "parse")) { + compile_error = compile_error ++ @typeName(BorderRight) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(BorderRight, "toCss")) { + compile_error = compile_error ++ @typeName(BorderRight) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(BorderRight, "eql")) { + compile_error = compile_error ++ @typeName(BorderRight) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(BorderBlock, "deepClone")) { + compile_error = compile_error ++ @typeName(BorderBlock) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(BorderBlock, "parse")) { + compile_error = compile_error ++ @typeName(BorderBlock) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(BorderBlock, "toCss")) { + compile_error = compile_error ++ @typeName(BorderBlock) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(BorderBlock, "eql")) { + compile_error = compile_error ++ @typeName(BorderBlock) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(BorderBlockStart, "deepClone")) { + compile_error = compile_error ++ @typeName(BorderBlockStart) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(BorderBlockStart, "parse")) { + compile_error = compile_error ++ @typeName(BorderBlockStart) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(BorderBlockStart, "toCss")) { + compile_error = compile_error ++ @typeName(BorderBlockStart) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(BorderBlockStart, "eql")) { + compile_error = compile_error ++ @typeName(BorderBlockStart) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(BorderBlockEnd, "deepClone")) { + compile_error = compile_error ++ @typeName(BorderBlockEnd) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(BorderBlockEnd, "parse")) { + compile_error = compile_error ++ @typeName(BorderBlockEnd) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(BorderBlockEnd, "toCss")) { + compile_error = compile_error ++ @typeName(BorderBlockEnd) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(BorderBlockEnd, "eql")) { + compile_error = compile_error ++ @typeName(BorderBlockEnd) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(BorderInline, "deepClone")) { + compile_error = compile_error ++ @typeName(BorderInline) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(BorderInline, "parse")) { + compile_error = compile_error ++ @typeName(BorderInline) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(BorderInline, "toCss")) { + compile_error = compile_error ++ @typeName(BorderInline) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(BorderInline, "eql")) { + compile_error = compile_error ++ @typeName(BorderInline) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(BorderInlineStart, "deepClone")) { + compile_error = compile_error ++ @typeName(BorderInlineStart) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(BorderInlineStart, "parse")) { + compile_error = compile_error ++ @typeName(BorderInlineStart) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(BorderInlineStart, "toCss")) { + compile_error = compile_error ++ @typeName(BorderInlineStart) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(BorderInlineStart, "eql")) { + compile_error = compile_error ++ @typeName(BorderInlineStart) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(BorderInlineEnd, "deepClone")) { + compile_error = compile_error ++ @typeName(BorderInlineEnd) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(BorderInlineEnd, "parse")) { + compile_error = compile_error ++ @typeName(BorderInlineEnd) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(BorderInlineEnd, "toCss")) { + compile_error = compile_error ++ @typeName(BorderInlineEnd) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(BorderInlineEnd, "eql")) { + compile_error = compile_error ++ @typeName(BorderInlineEnd) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(Outline, "deepClone")) { + compile_error = compile_error ++ @typeName(Outline) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(Outline, "parse")) { + compile_error = compile_error ++ @typeName(Outline) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(Outline, "toCss")) { + compile_error = compile_error ++ @typeName(Outline) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(Outline, "eql")) { + compile_error = compile_error ++ @typeName(Outline) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(CssColor, "deepClone")) { + compile_error = compile_error ++ @typeName(CssColor) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(CssColor, "parse")) { + compile_error = compile_error ++ @typeName(CssColor) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(CssColor, "toCss")) { + compile_error = compile_error ++ @typeName(CssColor) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(CssColor, "eql")) { + compile_error = compile_error ++ @typeName(CssColor) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(OutlineStyle, "deepClone")) { + compile_error = compile_error ++ @typeName(OutlineStyle) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(OutlineStyle, "parse")) { + compile_error = compile_error ++ @typeName(OutlineStyle) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(OutlineStyle, "toCss")) { + compile_error = compile_error ++ @typeName(OutlineStyle) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(OutlineStyle, "eql")) { + compile_error = compile_error ++ @typeName(OutlineStyle) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(BorderSideWidth, "deepClone")) { + compile_error = compile_error ++ @typeName(BorderSideWidth) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(BorderSideWidth, "parse")) { + compile_error = compile_error ++ @typeName(BorderSideWidth) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(BorderSideWidth, "toCss")) { + compile_error = compile_error ++ @typeName(BorderSideWidth) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(BorderSideWidth, "eql")) { + compile_error = compile_error ++ @typeName(BorderSideWidth) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(FlexDirection, "deepClone")) { + compile_error = compile_error ++ @typeName(FlexDirection) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(FlexDirection, "parse")) { + compile_error = compile_error ++ @typeName(FlexDirection) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(FlexDirection, "toCss")) { + compile_error = compile_error ++ @typeName(FlexDirection) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(FlexDirection, "eql")) { + compile_error = compile_error ++ @typeName(FlexDirection) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(FlexWrap, "deepClone")) { + compile_error = compile_error ++ @typeName(FlexWrap) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(FlexWrap, "parse")) { + compile_error = compile_error ++ @typeName(FlexWrap) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(FlexWrap, "toCss")) { + compile_error = compile_error ++ @typeName(FlexWrap) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(FlexWrap, "eql")) { + compile_error = compile_error ++ @typeName(FlexWrap) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(FlexFlow, "deepClone")) { + compile_error = compile_error ++ @typeName(FlexFlow) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(FlexFlow, "parse")) { + compile_error = compile_error ++ @typeName(FlexFlow) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(FlexFlow, "toCss")) { + compile_error = compile_error ++ @typeName(FlexFlow) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(FlexFlow, "eql")) { + compile_error = compile_error ++ @typeName(FlexFlow) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "deepClone")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "parse")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "toCss")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "eql")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(Flex, "deepClone")) { + compile_error = compile_error ++ @typeName(Flex) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(Flex, "parse")) { + compile_error = compile_error ++ @typeName(Flex) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(Flex, "toCss")) { + compile_error = compile_error ++ @typeName(Flex) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(Flex, "eql")) { + compile_error = compile_error ++ @typeName(Flex) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(AlignContent, "deepClone")) { + compile_error = compile_error ++ @typeName(AlignContent) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(AlignContent, "parse")) { + compile_error = compile_error ++ @typeName(AlignContent) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(AlignContent, "toCss")) { + compile_error = compile_error ++ @typeName(AlignContent) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(AlignContent, "eql")) { + compile_error = compile_error ++ @typeName(AlignContent) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(JustifyContent, "deepClone")) { + compile_error = compile_error ++ @typeName(JustifyContent) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(JustifyContent, "parse")) { + compile_error = compile_error ++ @typeName(JustifyContent) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(JustifyContent, "toCss")) { + compile_error = compile_error ++ @typeName(JustifyContent) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(JustifyContent, "eql")) { + compile_error = compile_error ++ @typeName(JustifyContent) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(PlaceContent, "deepClone")) { + compile_error = compile_error ++ @typeName(PlaceContent) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(PlaceContent, "parse")) { + compile_error = compile_error ++ @typeName(PlaceContent) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(PlaceContent, "toCss")) { + compile_error = compile_error ++ @typeName(PlaceContent) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(PlaceContent, "eql")) { + compile_error = compile_error ++ @typeName(PlaceContent) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(AlignSelf, "deepClone")) { + compile_error = compile_error ++ @typeName(AlignSelf) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(AlignSelf, "parse")) { + compile_error = compile_error ++ @typeName(AlignSelf) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(AlignSelf, "toCss")) { + compile_error = compile_error ++ @typeName(AlignSelf) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(AlignSelf, "eql")) { + compile_error = compile_error ++ @typeName(AlignSelf) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(JustifySelf, "deepClone")) { + compile_error = compile_error ++ @typeName(JustifySelf) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(JustifySelf, "parse")) { + compile_error = compile_error ++ @typeName(JustifySelf) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(JustifySelf, "toCss")) { + compile_error = compile_error ++ @typeName(JustifySelf) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(JustifySelf, "eql")) { + compile_error = compile_error ++ @typeName(JustifySelf) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(PlaceSelf, "deepClone")) { + compile_error = compile_error ++ @typeName(PlaceSelf) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(PlaceSelf, "parse")) { + compile_error = compile_error ++ @typeName(PlaceSelf) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(PlaceSelf, "toCss")) { + compile_error = compile_error ++ @typeName(PlaceSelf) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(PlaceSelf, "eql")) { + compile_error = compile_error ++ @typeName(PlaceSelf) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(AlignItems, "deepClone")) { + compile_error = compile_error ++ @typeName(AlignItems) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(AlignItems, "parse")) { + compile_error = compile_error ++ @typeName(AlignItems) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(AlignItems, "toCss")) { + compile_error = compile_error ++ @typeName(AlignItems) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(AlignItems, "eql")) { + compile_error = compile_error ++ @typeName(AlignItems) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(JustifyItems, "deepClone")) { + compile_error = compile_error ++ @typeName(JustifyItems) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(JustifyItems, "parse")) { + compile_error = compile_error ++ @typeName(JustifyItems) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(JustifyItems, "toCss")) { + compile_error = compile_error ++ @typeName(JustifyItems) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(JustifyItems, "eql")) { + compile_error = compile_error ++ @typeName(JustifyItems) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(PlaceItems, "deepClone")) { + compile_error = compile_error ++ @typeName(PlaceItems) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(PlaceItems, "parse")) { + compile_error = compile_error ++ @typeName(PlaceItems) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(PlaceItems, "toCss")) { + compile_error = compile_error ++ @typeName(PlaceItems) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(PlaceItems, "eql")) { + compile_error = compile_error ++ @typeName(PlaceItems) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(GapValue, "deepClone")) { + compile_error = compile_error ++ @typeName(GapValue) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(GapValue, "parse")) { + compile_error = compile_error ++ @typeName(GapValue) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(GapValue, "toCss")) { + compile_error = compile_error ++ @typeName(GapValue) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(GapValue, "eql")) { + compile_error = compile_error ++ @typeName(GapValue) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(GapValue, "deepClone")) { + compile_error = compile_error ++ @typeName(GapValue) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(GapValue, "parse")) { + compile_error = compile_error ++ @typeName(GapValue) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(GapValue, "toCss")) { + compile_error = compile_error ++ @typeName(GapValue) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(GapValue, "eql")) { + compile_error = compile_error ++ @typeName(GapValue) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(Gap, "deepClone")) { + compile_error = compile_error ++ @typeName(Gap) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(Gap, "parse")) { + compile_error = compile_error ++ @typeName(Gap) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(Gap, "toCss")) { + compile_error = compile_error ++ @typeName(Gap) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(Gap, "eql")) { + compile_error = compile_error ++ @typeName(Gap) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(BoxOrient, "deepClone")) { + compile_error = compile_error ++ @typeName(BoxOrient) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(BoxOrient, "parse")) { + compile_error = compile_error ++ @typeName(BoxOrient) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(BoxOrient, "toCss")) { + compile_error = compile_error ++ @typeName(BoxOrient) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(BoxOrient, "eql")) { + compile_error = compile_error ++ @typeName(BoxOrient) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(BoxDirection, "deepClone")) { + compile_error = compile_error ++ @typeName(BoxDirection) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(BoxDirection, "parse")) { + compile_error = compile_error ++ @typeName(BoxDirection) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(BoxDirection, "toCss")) { + compile_error = compile_error ++ @typeName(BoxDirection) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(BoxDirection, "eql")) { + compile_error = compile_error ++ @typeName(BoxDirection) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(BoxAlign, "deepClone")) { + compile_error = compile_error ++ @typeName(BoxAlign) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(BoxAlign, "parse")) { + compile_error = compile_error ++ @typeName(BoxAlign) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(BoxAlign, "toCss")) { + compile_error = compile_error ++ @typeName(BoxAlign) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(BoxAlign, "eql")) { + compile_error = compile_error ++ @typeName(BoxAlign) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(BoxPack, "deepClone")) { + compile_error = compile_error ++ @typeName(BoxPack) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(BoxPack, "parse")) { + compile_error = compile_error ++ @typeName(BoxPack) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(BoxPack, "toCss")) { + compile_error = compile_error ++ @typeName(BoxPack) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(BoxPack, "eql")) { + compile_error = compile_error ++ @typeName(BoxPack) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(BoxLines, "deepClone")) { + compile_error = compile_error ++ @typeName(BoxLines) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(BoxLines, "parse")) { + compile_error = compile_error ++ @typeName(BoxLines) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(BoxLines, "toCss")) { + compile_error = compile_error ++ @typeName(BoxLines) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(BoxLines, "eql")) { + compile_error = compile_error ++ @typeName(BoxLines) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(FlexPack, "deepClone")) { + compile_error = compile_error ++ @typeName(FlexPack) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(FlexPack, "parse")) { + compile_error = compile_error ++ @typeName(FlexPack) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(FlexPack, "toCss")) { + compile_error = compile_error ++ @typeName(FlexPack) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(FlexPack, "eql")) { + compile_error = compile_error ++ @typeName(FlexPack) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(BoxAlign, "deepClone")) { + compile_error = compile_error ++ @typeName(BoxAlign) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(BoxAlign, "parse")) { + compile_error = compile_error ++ @typeName(BoxAlign) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(BoxAlign, "toCss")) { + compile_error = compile_error ++ @typeName(BoxAlign) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(BoxAlign, "eql")) { + compile_error = compile_error ++ @typeName(BoxAlign) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(FlexItemAlign, "deepClone")) { + compile_error = compile_error ++ @typeName(FlexItemAlign) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(FlexItemAlign, "parse")) { + compile_error = compile_error ++ @typeName(FlexItemAlign) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(FlexItemAlign, "toCss")) { + compile_error = compile_error ++ @typeName(FlexItemAlign) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(FlexItemAlign, "eql")) { + compile_error = compile_error ++ @typeName(FlexItemAlign) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(FlexLinePack, "deepClone")) { + compile_error = compile_error ++ @typeName(FlexLinePack) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(FlexLinePack, "parse")) { + compile_error = compile_error ++ @typeName(FlexLinePack) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(FlexLinePack, "toCss")) { + compile_error = compile_error ++ @typeName(FlexLinePack) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(FlexLinePack, "eql")) { + compile_error = compile_error ++ @typeName(FlexLinePack) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "deepClone")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "parse")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "toCss")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "eql")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "deepClone")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "parse")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "toCss")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "eql")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "deepClone")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "parse")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "toCss")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "eql")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "deepClone")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "parse")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "toCss")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "eql")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "deepClone")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "parse")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "toCss")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "eql")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "deepClone")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "parse")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "toCss")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "eql")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "deepClone")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "parse")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "toCss")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "eql")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "deepClone")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "parse")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "toCss")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "eql")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "deepClone")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "parse")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "toCss")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "eql")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(MarginBlock, "deepClone")) { + compile_error = compile_error ++ @typeName(MarginBlock) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(MarginBlock, "parse")) { + compile_error = compile_error ++ @typeName(MarginBlock) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(MarginBlock, "toCss")) { + compile_error = compile_error ++ @typeName(MarginBlock) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(MarginBlock, "eql")) { + compile_error = compile_error ++ @typeName(MarginBlock) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(MarginInline, "deepClone")) { + compile_error = compile_error ++ @typeName(MarginInline) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(MarginInline, "parse")) { + compile_error = compile_error ++ @typeName(MarginInline) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(MarginInline, "toCss")) { + compile_error = compile_error ++ @typeName(MarginInline) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(MarginInline, "eql")) { + compile_error = compile_error ++ @typeName(MarginInline) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(Margin, "deepClone")) { + compile_error = compile_error ++ @typeName(Margin) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(Margin, "parse")) { + compile_error = compile_error ++ @typeName(Margin) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(Margin, "toCss")) { + compile_error = compile_error ++ @typeName(Margin) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(Margin, "eql")) { + compile_error = compile_error ++ @typeName(Margin) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "deepClone")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "parse")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "toCss")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "eql")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "deepClone")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "parse")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "toCss")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "eql")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "deepClone")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "parse")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "toCss")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "eql")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "deepClone")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "parse")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "toCss")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "eql")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "deepClone")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "parse")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "toCss")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "eql")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "deepClone")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "parse")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "toCss")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "eql")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "deepClone")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "parse")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "toCss")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "eql")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "deepClone")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "parse")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "toCss")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "eql")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(PaddingBlock, "deepClone")) { + compile_error = compile_error ++ @typeName(PaddingBlock) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(PaddingBlock, "parse")) { + compile_error = compile_error ++ @typeName(PaddingBlock) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(PaddingBlock, "toCss")) { + compile_error = compile_error ++ @typeName(PaddingBlock) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(PaddingBlock, "eql")) { + compile_error = compile_error ++ @typeName(PaddingBlock) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(PaddingInline, "deepClone")) { + compile_error = compile_error ++ @typeName(PaddingInline) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(PaddingInline, "parse")) { + compile_error = compile_error ++ @typeName(PaddingInline) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(PaddingInline, "toCss")) { + compile_error = compile_error ++ @typeName(PaddingInline) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(PaddingInline, "eql")) { + compile_error = compile_error ++ @typeName(PaddingInline) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(Padding, "deepClone")) { + compile_error = compile_error ++ @typeName(Padding) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(Padding, "parse")) { + compile_error = compile_error ++ @typeName(Padding) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(Padding, "toCss")) { + compile_error = compile_error ++ @typeName(Padding) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(Padding, "eql")) { + compile_error = compile_error ++ @typeName(Padding) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "deepClone")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "parse")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "toCss")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "eql")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "deepClone")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "parse")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "toCss")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "eql")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "deepClone")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "parse")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "toCss")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "eql")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "deepClone")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "parse")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "toCss")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "eql")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "deepClone")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "parse")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "toCss")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "eql")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "deepClone")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "parse")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "toCss")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "eql")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "deepClone")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "parse")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "toCss")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "eql")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "deepClone")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "parse")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "toCss")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "eql")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(ScrollMarginBlock, "deepClone")) { + compile_error = compile_error ++ @typeName(ScrollMarginBlock) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(ScrollMarginBlock, "parse")) { + compile_error = compile_error ++ @typeName(ScrollMarginBlock) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(ScrollMarginBlock, "toCss")) { + compile_error = compile_error ++ @typeName(ScrollMarginBlock) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(ScrollMarginBlock, "eql")) { + compile_error = compile_error ++ @typeName(ScrollMarginBlock) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(ScrollMarginInline, "deepClone")) { + compile_error = compile_error ++ @typeName(ScrollMarginInline) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(ScrollMarginInline, "parse")) { + compile_error = compile_error ++ @typeName(ScrollMarginInline) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(ScrollMarginInline, "toCss")) { + compile_error = compile_error ++ @typeName(ScrollMarginInline) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(ScrollMarginInline, "eql")) { + compile_error = compile_error ++ @typeName(ScrollMarginInline) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(ScrollMargin, "deepClone")) { + compile_error = compile_error ++ @typeName(ScrollMargin) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(ScrollMargin, "parse")) { + compile_error = compile_error ++ @typeName(ScrollMargin) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(ScrollMargin, "toCss")) { + compile_error = compile_error ++ @typeName(ScrollMargin) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(ScrollMargin, "eql")) { + compile_error = compile_error ++ @typeName(ScrollMargin) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "deepClone")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "parse")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "toCss")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "eql")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "deepClone")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "parse")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "toCss")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "eql")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "deepClone")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "parse")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "toCss")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "eql")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "deepClone")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "parse")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "toCss")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "eql")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "deepClone")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "parse")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "toCss")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "eql")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "deepClone")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "parse")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "toCss")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "eql")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "deepClone")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "parse")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "toCss")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "eql")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "deepClone")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "parse")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "toCss")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(LengthPercentageOrAuto, "eql")) { + compile_error = compile_error ++ @typeName(LengthPercentageOrAuto) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(ScrollPaddingBlock, "deepClone")) { + compile_error = compile_error ++ @typeName(ScrollPaddingBlock) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(ScrollPaddingBlock, "parse")) { + compile_error = compile_error ++ @typeName(ScrollPaddingBlock) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(ScrollPaddingBlock, "toCss")) { + compile_error = compile_error ++ @typeName(ScrollPaddingBlock) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(ScrollPaddingBlock, "eql")) { + compile_error = compile_error ++ @typeName(ScrollPaddingBlock) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(ScrollPaddingInline, "deepClone")) { + compile_error = compile_error ++ @typeName(ScrollPaddingInline) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(ScrollPaddingInline, "parse")) { + compile_error = compile_error ++ @typeName(ScrollPaddingInline) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(ScrollPaddingInline, "toCss")) { + compile_error = compile_error ++ @typeName(ScrollPaddingInline) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(ScrollPaddingInline, "eql")) { + compile_error = compile_error ++ @typeName(ScrollPaddingInline) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(ScrollPadding, "deepClone")) { + compile_error = compile_error ++ @typeName(ScrollPadding) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(ScrollPadding, "parse")) { + compile_error = compile_error ++ @typeName(ScrollPadding) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(ScrollPadding, "toCss")) { + compile_error = compile_error ++ @typeName(ScrollPadding) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(ScrollPadding, "eql")) { + compile_error = compile_error ++ @typeName(ScrollPadding) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(FontWeight, "deepClone")) { + compile_error = compile_error ++ @typeName(FontWeight) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(FontWeight, "parse")) { + compile_error = compile_error ++ @typeName(FontWeight) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(FontWeight, "toCss")) { + compile_error = compile_error ++ @typeName(FontWeight) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(FontWeight, "eql")) { + compile_error = compile_error ++ @typeName(FontWeight) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(FontSize, "deepClone")) { + compile_error = compile_error ++ @typeName(FontSize) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(FontSize, "parse")) { + compile_error = compile_error ++ @typeName(FontSize) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(FontSize, "toCss")) { + compile_error = compile_error ++ @typeName(FontSize) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(FontSize, "eql")) { + compile_error = compile_error ++ @typeName(FontSize) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(FontStretch, "deepClone")) { + compile_error = compile_error ++ @typeName(FontStretch) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(FontStretch, "parse")) { + compile_error = compile_error ++ @typeName(FontStretch) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(FontStretch, "toCss")) { + compile_error = compile_error ++ @typeName(FontStretch) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(FontStretch, "eql")) { + compile_error = compile_error ++ @typeName(FontStretch) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(BabyList(FontFamily), "deepClone")) { + compile_error = compile_error ++ @typeName(BabyList(FontFamily)) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(BabyList(FontFamily), "parse")) { + compile_error = compile_error ++ @typeName(BabyList(FontFamily)) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(BabyList(FontFamily), "toCss")) { + compile_error = compile_error ++ @typeName(BabyList(FontFamily)) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(BabyList(FontFamily), "eql")) { + compile_error = compile_error ++ @typeName(BabyList(FontFamily)) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(FontStyle, "deepClone")) { + compile_error = compile_error ++ @typeName(FontStyle) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(FontStyle, "parse")) { + compile_error = compile_error ++ @typeName(FontStyle) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(FontStyle, "toCss")) { + compile_error = compile_error ++ @typeName(FontStyle) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(FontStyle, "eql")) { + compile_error = compile_error ++ @typeName(FontStyle) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(FontVariantCaps, "deepClone")) { + compile_error = compile_error ++ @typeName(FontVariantCaps) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(FontVariantCaps, "parse")) { + compile_error = compile_error ++ @typeName(FontVariantCaps) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(FontVariantCaps, "toCss")) { + compile_error = compile_error ++ @typeName(FontVariantCaps) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(FontVariantCaps, "eql")) { + compile_error = compile_error ++ @typeName(FontVariantCaps) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(LineHeight, "deepClone")) { + compile_error = compile_error ++ @typeName(LineHeight) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(LineHeight, "parse")) { + compile_error = compile_error ++ @typeName(LineHeight) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(LineHeight, "toCss")) { + compile_error = compile_error ++ @typeName(LineHeight) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(LineHeight, "eql")) { + compile_error = compile_error ++ @typeName(LineHeight) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(Font, "deepClone")) { + compile_error = compile_error ++ @typeName(Font) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(Font, "parse")) { + compile_error = compile_error ++ @typeName(Font) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(Font, "toCss")) { + compile_error = compile_error ++ @typeName(Font) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(Font, "eql")) { + compile_error = compile_error ++ @typeName(Font) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(CssColor, "deepClone")) { + compile_error = compile_error ++ @typeName(CssColor) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(CssColor, "parse")) { + compile_error = compile_error ++ @typeName(CssColor) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(CssColor, "toCss")) { + compile_error = compile_error ++ @typeName(CssColor) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(CssColor, "eql")) { + compile_error = compile_error ++ @typeName(CssColor) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(CssColor, "deepClone")) { + compile_error = compile_error ++ @typeName(CssColor) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(CssColor, "parse")) { + compile_error = compile_error ++ @typeName(CssColor) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(CssColor, "toCss")) { + compile_error = compile_error ++ @typeName(CssColor) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(CssColor, "eql")) { + compile_error = compile_error ++ @typeName(CssColor) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(Direction, "deepClone")) { + compile_error = compile_error ++ @typeName(Direction) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(Direction, "parse")) { + compile_error = compile_error ++ @typeName(Direction) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(Direction, "toCss")) { + compile_error = compile_error ++ @typeName(Direction) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(Direction, "eql")) { + compile_error = compile_error ++ @typeName(Direction) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(Composes, "deepClone")) { + compile_error = compile_error ++ @typeName(Composes) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(Composes, "parse")) { + compile_error = compile_error ++ @typeName(Composes) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(Composes, "toCss")) { + compile_error = compile_error ++ @typeName(Composes) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(Composes, "eql")) { + compile_error = compile_error ++ @typeName(Composes) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(SmallList(Image, 1), "deepClone")) { + compile_error = compile_error ++ @typeName(SmallList(Image, 1)) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(SmallList(Image, 1), "parse")) { + compile_error = compile_error ++ @typeName(SmallList(Image, 1)) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(SmallList(Image, 1), "toCss")) { + compile_error = compile_error ++ @typeName(SmallList(Image, 1)) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(SmallList(Image, 1), "eql")) { + compile_error = compile_error ++ @typeName(SmallList(Image, 1)) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(SmallList(MaskMode, 1), "deepClone")) { + compile_error = compile_error ++ @typeName(SmallList(MaskMode, 1)) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(SmallList(MaskMode, 1), "parse")) { + compile_error = compile_error ++ @typeName(SmallList(MaskMode, 1)) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(SmallList(MaskMode, 1), "toCss")) { + compile_error = compile_error ++ @typeName(SmallList(MaskMode, 1)) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(SmallList(MaskMode, 1), "eql")) { + compile_error = compile_error ++ @typeName(SmallList(MaskMode, 1)) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(SmallList(BackgroundRepeat, 1), "deepClone")) { + compile_error = compile_error ++ @typeName(SmallList(BackgroundRepeat, 1)) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(SmallList(BackgroundRepeat, 1), "parse")) { + compile_error = compile_error ++ @typeName(SmallList(BackgroundRepeat, 1)) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(SmallList(BackgroundRepeat, 1), "toCss")) { + compile_error = compile_error ++ @typeName(SmallList(BackgroundRepeat, 1)) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(SmallList(BackgroundRepeat, 1), "eql")) { + compile_error = compile_error ++ @typeName(SmallList(BackgroundRepeat, 1)) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(SmallList(HorizontalPosition, 1), "deepClone")) { + compile_error = compile_error ++ @typeName(SmallList(HorizontalPosition, 1)) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(SmallList(HorizontalPosition, 1), "parse")) { + compile_error = compile_error ++ @typeName(SmallList(HorizontalPosition, 1)) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(SmallList(HorizontalPosition, 1), "toCss")) { + compile_error = compile_error ++ @typeName(SmallList(HorizontalPosition, 1)) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(SmallList(HorizontalPosition, 1), "eql")) { + compile_error = compile_error ++ @typeName(SmallList(HorizontalPosition, 1)) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(SmallList(VerticalPosition, 1), "deepClone")) { + compile_error = compile_error ++ @typeName(SmallList(VerticalPosition, 1)) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(SmallList(VerticalPosition, 1), "parse")) { + compile_error = compile_error ++ @typeName(SmallList(VerticalPosition, 1)) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(SmallList(VerticalPosition, 1), "toCss")) { + compile_error = compile_error ++ @typeName(SmallList(VerticalPosition, 1)) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(SmallList(VerticalPosition, 1), "eql")) { + compile_error = compile_error ++ @typeName(SmallList(VerticalPosition, 1)) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(SmallList(Position, 1), "deepClone")) { + compile_error = compile_error ++ @typeName(SmallList(Position, 1)) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(SmallList(Position, 1), "parse")) { + compile_error = compile_error ++ @typeName(SmallList(Position, 1)) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(SmallList(Position, 1), "toCss")) { + compile_error = compile_error ++ @typeName(SmallList(Position, 1)) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(SmallList(Position, 1), "eql")) { + compile_error = compile_error ++ @typeName(SmallList(Position, 1)) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(SmallList(MaskClip, 1), "deepClone")) { + compile_error = compile_error ++ @typeName(SmallList(MaskClip, 1)) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(SmallList(MaskClip, 1), "parse")) { + compile_error = compile_error ++ @typeName(SmallList(MaskClip, 1)) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(SmallList(MaskClip, 1), "toCss")) { + compile_error = compile_error ++ @typeName(SmallList(MaskClip, 1)) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(SmallList(MaskClip, 1), "eql")) { + compile_error = compile_error ++ @typeName(SmallList(MaskClip, 1)) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(SmallList(GeometryBox, 1), "deepClone")) { + compile_error = compile_error ++ @typeName(SmallList(GeometryBox, 1)) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(SmallList(GeometryBox, 1), "parse")) { + compile_error = compile_error ++ @typeName(SmallList(GeometryBox, 1)) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(SmallList(GeometryBox, 1), "toCss")) { + compile_error = compile_error ++ @typeName(SmallList(GeometryBox, 1)) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(SmallList(GeometryBox, 1), "eql")) { + compile_error = compile_error ++ @typeName(SmallList(GeometryBox, 1)) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(SmallList(BackgroundSize, 1), "deepClone")) { + compile_error = compile_error ++ @typeName(SmallList(BackgroundSize, 1)) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(SmallList(BackgroundSize, 1), "parse")) { + compile_error = compile_error ++ @typeName(SmallList(BackgroundSize, 1)) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(SmallList(BackgroundSize, 1), "toCss")) { + compile_error = compile_error ++ @typeName(SmallList(BackgroundSize, 1)) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(SmallList(BackgroundSize, 1), "eql")) { + compile_error = compile_error ++ @typeName(SmallList(BackgroundSize, 1)) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(SmallList(MaskComposite, 1), "deepClone")) { + compile_error = compile_error ++ @typeName(SmallList(MaskComposite, 1)) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(SmallList(MaskComposite, 1), "parse")) { + compile_error = compile_error ++ @typeName(SmallList(MaskComposite, 1)) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(SmallList(MaskComposite, 1), "toCss")) { + compile_error = compile_error ++ @typeName(SmallList(MaskComposite, 1)) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(SmallList(MaskComposite, 1), "eql")) { + compile_error = compile_error ++ @typeName(SmallList(MaskComposite, 1)) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(MaskType, "deepClone")) { + compile_error = compile_error ++ @typeName(MaskType) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(MaskType, "parse")) { + compile_error = compile_error ++ @typeName(MaskType) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(MaskType, "toCss")) { + compile_error = compile_error ++ @typeName(MaskType) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(MaskType, "eql")) { + compile_error = compile_error ++ @typeName(MaskType) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(SmallList(Mask, 1), "deepClone")) { + compile_error = compile_error ++ @typeName(SmallList(Mask, 1)) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(SmallList(Mask, 1), "parse")) { + compile_error = compile_error ++ @typeName(SmallList(Mask, 1)) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(SmallList(Mask, 1), "toCss")) { + compile_error = compile_error ++ @typeName(SmallList(Mask, 1)) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(SmallList(Mask, 1), "eql")) { + compile_error = compile_error ++ @typeName(SmallList(Mask, 1)) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(Image, "deepClone")) { + compile_error = compile_error ++ @typeName(Image) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(Image, "parse")) { + compile_error = compile_error ++ @typeName(Image) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(Image, "toCss")) { + compile_error = compile_error ++ @typeName(Image) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(Image, "eql")) { + compile_error = compile_error ++ @typeName(Image) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(MaskBorderMode, "deepClone")) { + compile_error = compile_error ++ @typeName(MaskBorderMode) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(MaskBorderMode, "parse")) { + compile_error = compile_error ++ @typeName(MaskBorderMode) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(MaskBorderMode, "toCss")) { + compile_error = compile_error ++ @typeName(MaskBorderMode) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(MaskBorderMode, "eql")) { + compile_error = compile_error ++ @typeName(MaskBorderMode) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(BorderImageSlice, "deepClone")) { + compile_error = compile_error ++ @typeName(BorderImageSlice) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(BorderImageSlice, "parse")) { + compile_error = compile_error ++ @typeName(BorderImageSlice) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(BorderImageSlice, "toCss")) { + compile_error = compile_error ++ @typeName(BorderImageSlice) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(BorderImageSlice, "eql")) { + compile_error = compile_error ++ @typeName(BorderImageSlice) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(Rect(BorderImageSideWidth), "deepClone")) { + compile_error = compile_error ++ @typeName(Rect(BorderImageSideWidth)) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(Rect(BorderImageSideWidth), "parse")) { + compile_error = compile_error ++ @typeName(Rect(BorderImageSideWidth)) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(Rect(BorderImageSideWidth), "toCss")) { + compile_error = compile_error ++ @typeName(Rect(BorderImageSideWidth)) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(Rect(BorderImageSideWidth), "eql")) { + compile_error = compile_error ++ @typeName(Rect(BorderImageSideWidth)) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(Rect(LengthOrNumber), "deepClone")) { + compile_error = compile_error ++ @typeName(Rect(LengthOrNumber)) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(Rect(LengthOrNumber), "parse")) { + compile_error = compile_error ++ @typeName(Rect(LengthOrNumber)) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(Rect(LengthOrNumber), "toCss")) { + compile_error = compile_error ++ @typeName(Rect(LengthOrNumber)) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(Rect(LengthOrNumber), "eql")) { + compile_error = compile_error ++ @typeName(Rect(LengthOrNumber)) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(BorderImageRepeat, "deepClone")) { + compile_error = compile_error ++ @typeName(BorderImageRepeat) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(BorderImageRepeat, "parse")) { + compile_error = compile_error ++ @typeName(BorderImageRepeat) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(BorderImageRepeat, "toCss")) { + compile_error = compile_error ++ @typeName(BorderImageRepeat) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(BorderImageRepeat, "eql")) { + compile_error = compile_error ++ @typeName(BorderImageRepeat) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(MaskBorder, "deepClone")) { + compile_error = compile_error ++ @typeName(MaskBorder) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(MaskBorder, "parse")) { + compile_error = compile_error ++ @typeName(MaskBorder) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(MaskBorder, "toCss")) { + compile_error = compile_error ++ @typeName(MaskBorder) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(MaskBorder, "eql")) { + compile_error = compile_error ++ @typeName(MaskBorder) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(SmallList(WebKitMaskComposite, 1), "deepClone")) { + compile_error = compile_error ++ @typeName(SmallList(WebKitMaskComposite, 1)) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(SmallList(WebKitMaskComposite, 1), "parse")) { + compile_error = compile_error ++ @typeName(SmallList(WebKitMaskComposite, 1)) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(SmallList(WebKitMaskComposite, 1), "toCss")) { + compile_error = compile_error ++ @typeName(SmallList(WebKitMaskComposite, 1)) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(SmallList(WebKitMaskComposite, 1), "eql")) { + compile_error = compile_error ++ @typeName(SmallList(WebKitMaskComposite, 1)) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(SmallList(WebKitMaskSourceType, 1), "deepClone")) { + compile_error = compile_error ++ @typeName(SmallList(WebKitMaskSourceType, 1)) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(SmallList(WebKitMaskSourceType, 1), "parse")) { + compile_error = compile_error ++ @typeName(SmallList(WebKitMaskSourceType, 1)) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(SmallList(WebKitMaskSourceType, 1), "toCss")) { + compile_error = compile_error ++ @typeName(SmallList(WebKitMaskSourceType, 1)) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(SmallList(WebKitMaskSourceType, 1), "eql")) { + compile_error = compile_error ++ @typeName(SmallList(WebKitMaskSourceType, 1)) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(BorderImage, "deepClone")) { + compile_error = compile_error ++ @typeName(BorderImage) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(BorderImage, "parse")) { + compile_error = compile_error ++ @typeName(BorderImage) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(BorderImage, "toCss")) { + compile_error = compile_error ++ @typeName(BorderImage) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(BorderImage, "eql")) { + compile_error = compile_error ++ @typeName(BorderImage) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(Image, "deepClone")) { + compile_error = compile_error ++ @typeName(Image) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(Image, "parse")) { + compile_error = compile_error ++ @typeName(Image) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(Image, "toCss")) { + compile_error = compile_error ++ @typeName(Image) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(Image, "eql")) { + compile_error = compile_error ++ @typeName(Image) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(BorderImageSlice, "deepClone")) { + compile_error = compile_error ++ @typeName(BorderImageSlice) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(BorderImageSlice, "parse")) { + compile_error = compile_error ++ @typeName(BorderImageSlice) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(BorderImageSlice, "toCss")) { + compile_error = compile_error ++ @typeName(BorderImageSlice) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(BorderImageSlice, "eql")) { + compile_error = compile_error ++ @typeName(BorderImageSlice) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(Rect(BorderImageSideWidth), "deepClone")) { + compile_error = compile_error ++ @typeName(Rect(BorderImageSideWidth)) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(Rect(BorderImageSideWidth), "parse")) { + compile_error = compile_error ++ @typeName(Rect(BorderImageSideWidth)) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(Rect(BorderImageSideWidth), "toCss")) { + compile_error = compile_error ++ @typeName(Rect(BorderImageSideWidth)) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(Rect(BorderImageSideWidth), "eql")) { + compile_error = compile_error ++ @typeName(Rect(BorderImageSideWidth)) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(Rect(LengthOrNumber), "deepClone")) { + compile_error = compile_error ++ @typeName(Rect(LengthOrNumber)) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(Rect(LengthOrNumber), "parse")) { + compile_error = compile_error ++ @typeName(Rect(LengthOrNumber)) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(Rect(LengthOrNumber), "toCss")) { + compile_error = compile_error ++ @typeName(Rect(LengthOrNumber)) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(Rect(LengthOrNumber), "eql")) { + compile_error = compile_error ++ @typeName(Rect(LengthOrNumber)) ++ ": does not have a eql() function.\n"; + } + + if (!@hasDecl(BorderImageRepeat, "deepClone")) { + compile_error = compile_error ++ @typeName(BorderImageRepeat) ++ ": does not have a deepClone() function.\n"; + } + + if (!@hasDecl(BorderImageRepeat, "parse")) { + compile_error = compile_error ++ @typeName(BorderImageRepeat) ++ ": does not have a parse() function.\n"; + } + + if (!@hasDecl(BorderImageRepeat, "toCss")) { + compile_error = compile_error ++ @typeName(BorderImageRepeat) ++ ": does not have a toCss() function.\n"; + } + + if (!@hasDecl(BorderImageRepeat, "eql")) { + compile_error = compile_error ++ @typeName(BorderImageRepeat) ++ ": does not have a eql() function.\n"; + } + + const final_compile_error = compile_error; + break :compile_error final_compile_error; + }; + if (compile_error.len > 0) { + @compileError(compile_error); + } + } + /// Parses a CSS property by name. pub fn parse(property_id: PropertyId, input: *css.Parser, options: *const css.ParserOptions) Result(Property) { const state = input.state(); @@ -284,6 +4032,90 @@ pub const Property = union(PropertyIdTag) { } } }, + .@"background-image" => { + if (css.generic.parseWithOptions(SmallList(Image, 1), input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"background-image" = c } }; + } + } + }, + .@"background-position-x" => { + if (css.generic.parseWithOptions(SmallList(css_values.position.HorizontalPosition, 1), input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"background-position-x" = c } }; + } + } + }, + .@"background-position-y" => { + if (css.generic.parseWithOptions(SmallList(css_values.position.HorizontalPosition, 1), input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"background-position-y" = c } }; + } + } + }, + .@"background-position" => { + if (css.generic.parseWithOptions(SmallList(background.BackgroundPosition, 1), input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"background-position" = c } }; + } + } + }, + .@"background-size" => { + if (css.generic.parseWithOptions(SmallList(background.BackgroundSize, 1), input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"background-size" = c } }; + } + } + }, + .@"background-repeat" => { + if (css.generic.parseWithOptions(SmallList(background.BackgroundSize, 1), input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"background-repeat" = c } }; + } + } + }, + .@"background-attachment" => { + if (css.generic.parseWithOptions(SmallList(background.BackgroundAttachment, 1), input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"background-attachment" = c } }; + } + } + }, + .@"background-clip" => |pre| { + if (css.generic.parseWithOptions(SmallList(background.BackgroundAttachment, 1), input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"background-clip" = .{ c, pre } } }; + } + } + }, + .@"background-origin" => { + if (css.generic.parseWithOptions(SmallList(background.BackgroundOrigin, 1), input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"background-origin" = c } }; + } + } + }, + .background => { + if (css.generic.parseWithOptions(SmallList(background.Background, 1), input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .background = c } }; + } + } + }, + .@"box-shadow" => |pre| { + if (css.generic.parseWithOptions(SmallList(box_shadow.BoxShadow, 1), input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"box-shadow" = .{ c, pre } } }; + } + } + }, + .opacity => { + if (css.generic.parseWithOptions(css.css_values.alpha.AlphaValue, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .opacity = c } }; + } + } + }, .color => { if (css.generic.parseWithOptions(CssColor, input, options).asValue()) |c| { if (input.expectExhausted().isOk()) { @@ -291,6 +4123,230 @@ pub const Property = union(PropertyIdTag) { } } }, + .display => { + if (css.generic.parseWithOptions(display.Display, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .display = c } }; + } + } + }, + .visibility => { + if (css.generic.parseWithOptions(display.Visibility, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .visibility = c } }; + } + } + }, + .width => { + if (css.generic.parseWithOptions(size.Size, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .width = c } }; + } + } + }, + .height => { + if (css.generic.parseWithOptions(size.Size, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .height = c } }; + } + } + }, + .@"min-width" => { + if (css.generic.parseWithOptions(size.Size, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"min-width" = c } }; + } + } + }, + .@"min-height" => { + if (css.generic.parseWithOptions(size.Size, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"min-height" = c } }; + } + } + }, + .@"max-width" => { + if (css.generic.parseWithOptions(size.MaxSize, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"max-width" = c } }; + } + } + }, + .@"max-height" => { + if (css.generic.parseWithOptions(size.MaxSize, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"max-height" = c } }; + } + } + }, + .@"block-size" => { + if (css.generic.parseWithOptions(size.Size, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"block-size" = c } }; + } + } + }, + .@"inline-size" => { + if (css.generic.parseWithOptions(size.Size, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"inline-size" = c } }; + } + } + }, + .@"min-block-size" => { + if (css.generic.parseWithOptions(size.Size, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"min-block-size" = c } }; + } + } + }, + .@"min-inline-size" => { + if (css.generic.parseWithOptions(size.Size, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"min-inline-size" = c } }; + } + } + }, + .@"max-block-size" => { + if (css.generic.parseWithOptions(size.MaxSize, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"max-block-size" = c } }; + } + } + }, + .@"max-inline-size" => { + if (css.generic.parseWithOptions(size.MaxSize, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"max-inline-size" = c } }; + } + } + }, + .@"box-sizing" => |pre| { + if (css.generic.parseWithOptions(size.BoxSizing, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"box-sizing" = .{ c, pre } } }; + } + } + }, + .@"aspect-ratio" => { + if (css.generic.parseWithOptions(size.AspectRatio, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"aspect-ratio" = c } }; + } + } + }, + .overflow => { + if (css.generic.parseWithOptions(overflow.Overflow, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .overflow = c } }; + } + } + }, + .@"overflow-x" => { + if (css.generic.parseWithOptions(overflow.OverflowKeyword, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"overflow-x" = c } }; + } + } + }, + .@"overflow-y" => { + if (css.generic.parseWithOptions(overflow.OverflowKeyword, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"overflow-y" = c } }; + } + } + }, + .@"text-overflow" => |pre| { + if (css.generic.parseWithOptions(overflow.TextOverflow, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"text-overflow" = .{ c, pre } } }; + } + } + }, + .position => { + if (css.generic.parseWithOptions(position.Position, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .position = c } }; + } + } + }, + .top => { + if (css.generic.parseWithOptions(LengthPercentageOrAuto, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .top = c } }; + } + } + }, + .bottom => { + if (css.generic.parseWithOptions(LengthPercentageOrAuto, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .bottom = c } }; + } + } + }, + .left => { + if (css.generic.parseWithOptions(LengthPercentageOrAuto, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .left = c } }; + } + } + }, + .right => { + if (css.generic.parseWithOptions(LengthPercentageOrAuto, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .right = c } }; + } + } + }, + .@"inset-block-start" => { + if (css.generic.parseWithOptions(LengthPercentageOrAuto, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"inset-block-start" = c } }; + } + } + }, + .@"inset-block-end" => { + if (css.generic.parseWithOptions(LengthPercentageOrAuto, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"inset-block-end" = c } }; + } + } + }, + .@"inset-inline-start" => { + if (css.generic.parseWithOptions(LengthPercentageOrAuto, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"inset-inline-start" = c } }; + } + } + }, + .@"inset-inline-end" => { + if (css.generic.parseWithOptions(LengthPercentageOrAuto, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"inset-inline-end" = c } }; + } + } + }, + .@"inset-block" => { + if (css.generic.parseWithOptions(margin_padding.InsetBlock, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"inset-block" = c } }; + } + } + }, + .@"inset-inline" => { + if (css.generic.parseWithOptions(margin_padding.InsetInline, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"inset-inline" = c } }; + } + } + }, + .inset => { + if (css.generic.parseWithOptions(margin_padding.Inset, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .inset = c } }; + } + } + }, .@"border-spacing" => { if (css.generic.parseWithOptions(css.css_values.size.Size2D(Length), input, options).asValue()) |c| { if (input.expectExhausted().isOk()) { @@ -396,6 +4452,20 @@ pub const Property = union(PropertyIdTag) { } } }, + .@"border-inline-start-style" => { + if (css.generic.parseWithOptions(border.LineStyle, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"border-inline-start-style" = c } }; + } + } + }, + .@"border-inline-end-style" => { + if (css.generic.parseWithOptions(border.LineStyle, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"border-inline-end-style" = c } }; + } + } + }, .@"border-top-width" => { if (css.generic.parseWithOptions(BorderSideWidth, input, options).asValue()) |c| { if (input.expectExhausted().isOk()) { @@ -424,6 +4494,286 @@ pub const Property = union(PropertyIdTag) { } } }, + .@"border-block-start-width" => { + if (css.generic.parseWithOptions(BorderSideWidth, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"border-block-start-width" = c } }; + } + } + }, + .@"border-block-end-width" => { + if (css.generic.parseWithOptions(BorderSideWidth, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"border-block-end-width" = c } }; + } + } + }, + .@"border-inline-start-width" => { + if (css.generic.parseWithOptions(BorderSideWidth, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"border-inline-start-width" = c } }; + } + } + }, + .@"border-inline-end-width" => { + if (css.generic.parseWithOptions(BorderSideWidth, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"border-inline-end-width" = c } }; + } + } + }, + .@"border-top-left-radius" => |pre| { + if (css.generic.parseWithOptions(Size2D(LengthPercentage), input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"border-top-left-radius" = .{ c, pre } } }; + } + } + }, + .@"border-top-right-radius" => |pre| { + if (css.generic.parseWithOptions(Size2D(LengthPercentage), input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"border-top-right-radius" = .{ c, pre } } }; + } + } + }, + .@"border-bottom-left-radius" => |pre| { + if (css.generic.parseWithOptions(Size2D(LengthPercentage), input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"border-bottom-left-radius" = .{ c, pre } } }; + } + } + }, + .@"border-bottom-right-radius" => |pre| { + if (css.generic.parseWithOptions(Size2D(LengthPercentage), input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"border-bottom-right-radius" = .{ c, pre } } }; + } + } + }, + .@"border-start-start-radius" => { + if (css.generic.parseWithOptions(Size2D(LengthPercentage), input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"border-start-start-radius" = c } }; + } + } + }, + .@"border-start-end-radius" => { + if (css.generic.parseWithOptions(Size2D(LengthPercentage), input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"border-start-end-radius" = c } }; + } + } + }, + .@"border-end-start-radius" => { + if (css.generic.parseWithOptions(Size2D(LengthPercentage), input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"border-end-start-radius" = c } }; + } + } + }, + .@"border-end-end-radius" => { + if (css.generic.parseWithOptions(Size2D(LengthPercentage), input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"border-end-end-radius" = c } }; + } + } + }, + .@"border-radius" => |pre| { + if (css.generic.parseWithOptions(BorderRadius, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"border-radius" = .{ c, pre } } }; + } + } + }, + .@"border-image-source" => { + if (css.generic.parseWithOptions(Image, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"border-image-source" = c } }; + } + } + }, + .@"border-image-outset" => { + if (css.generic.parseWithOptions(Rect(LengthOrNumber), input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"border-image-outset" = c } }; + } + } + }, + .@"border-image-repeat" => { + if (css.generic.parseWithOptions(BorderImageRepeat, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"border-image-repeat" = c } }; + } + } + }, + .@"border-image-width" => { + if (css.generic.parseWithOptions(Rect(BorderImageSideWidth), input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"border-image-width" = c } }; + } + } + }, + .@"border-image-slice" => { + if (css.generic.parseWithOptions(BorderImageSlice, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"border-image-slice" = c } }; + } + } + }, + .@"border-image" => |pre| { + if (css.generic.parseWithOptions(BorderImage, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"border-image" = .{ c, pre } } }; + } + } + }, + .@"border-color" => { + if (css.generic.parseWithOptions(BorderColor, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"border-color" = c } }; + } + } + }, + .@"border-style" => { + if (css.generic.parseWithOptions(BorderStyle, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"border-style" = c } }; + } + } + }, + .@"border-width" => { + if (css.generic.parseWithOptions(BorderWidth, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"border-width" = c } }; + } + } + }, + .@"border-block-color" => { + if (css.generic.parseWithOptions(BorderBlockColor, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"border-block-color" = c } }; + } + } + }, + .@"border-block-style" => { + if (css.generic.parseWithOptions(BorderBlockStyle, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"border-block-style" = c } }; + } + } + }, + .@"border-block-width" => { + if (css.generic.parseWithOptions(BorderBlockWidth, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"border-block-width" = c } }; + } + } + }, + .@"border-inline-color" => { + if (css.generic.parseWithOptions(BorderInlineColor, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"border-inline-color" = c } }; + } + } + }, + .@"border-inline-style" => { + if (css.generic.parseWithOptions(BorderInlineStyle, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"border-inline-style" = c } }; + } + } + }, + .@"border-inline-width" => { + if (css.generic.parseWithOptions(BorderInlineWidth, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"border-inline-width" = c } }; + } + } + }, + .border => { + if (css.generic.parseWithOptions(Border, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .border = c } }; + } + } + }, + .@"border-top" => { + if (css.generic.parseWithOptions(BorderTop, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"border-top" = c } }; + } + } + }, + .@"border-bottom" => { + if (css.generic.parseWithOptions(BorderBottom, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"border-bottom" = c } }; + } + } + }, + .@"border-left" => { + if (css.generic.parseWithOptions(BorderLeft, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"border-left" = c } }; + } + } + }, + .@"border-right" => { + if (css.generic.parseWithOptions(BorderRight, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"border-right" = c } }; + } + } + }, + .@"border-block" => { + if (css.generic.parseWithOptions(BorderBlock, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"border-block" = c } }; + } + } + }, + .@"border-block-start" => { + if (css.generic.parseWithOptions(BorderBlockStart, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"border-block-start" = c } }; + } + } + }, + .@"border-block-end" => { + if (css.generic.parseWithOptions(BorderBlockEnd, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"border-block-end" = c } }; + } + } + }, + .@"border-inline" => { + if (css.generic.parseWithOptions(BorderInline, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"border-inline" = c } }; + } + } + }, + .@"border-inline-start" => { + if (css.generic.parseWithOptions(BorderInlineStart, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"border-inline-start" = c } }; + } + } + }, + .@"border-inline-end" => { + if (css.generic.parseWithOptions(BorderInlineEnd, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"border-inline-end" = c } }; + } + } + }, + .outline => { + if (css.generic.parseWithOptions(Outline, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .outline = c } }; + } + } + }, .@"outline-color" => { if (css.generic.parseWithOptions(CssColor, input, options).asValue()) |c| { if (input.expectExhausted().isOk()) { @@ -431,6 +4781,637 @@ pub const Property = union(PropertyIdTag) { } } }, + .@"outline-style" => { + if (css.generic.parseWithOptions(OutlineStyle, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"outline-style" = c } }; + } + } + }, + .@"outline-width" => { + if (css.generic.parseWithOptions(BorderSideWidth, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"outline-width" = c } }; + } + } + }, + .@"flex-direction" => |pre| { + if (css.generic.parseWithOptions(FlexDirection, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"flex-direction" = .{ c, pre } } }; + } + } + }, + .@"flex-wrap" => |pre| { + if (css.generic.parseWithOptions(FlexWrap, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"flex-wrap" = .{ c, pre } } }; + } + } + }, + .@"flex-flow" => |pre| { + if (css.generic.parseWithOptions(FlexFlow, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"flex-flow" = .{ c, pre } } }; + } + } + }, + .@"flex-grow" => |pre| { + if (css.generic.parseWithOptions(CSSNumber, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"flex-grow" = .{ c, pre } } }; + } + } + }, + .@"flex-shrink" => |pre| { + if (css.generic.parseWithOptions(CSSNumber, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"flex-shrink" = .{ c, pre } } }; + } + } + }, + .@"flex-basis" => |pre| { + if (css.generic.parseWithOptions(LengthPercentageOrAuto, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"flex-basis" = .{ c, pre } } }; + } + } + }, + .flex => |pre| { + if (css.generic.parseWithOptions(Flex, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .flex = .{ c, pre } } }; + } + } + }, + .order => |pre| { + if (css.generic.parseWithOptions(CSSInteger, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .order = .{ c, pre } } }; + } + } + }, + .@"align-content" => |pre| { + if (css.generic.parseWithOptions(AlignContent, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"align-content" = .{ c, pre } } }; + } + } + }, + .@"justify-content" => |pre| { + if (css.generic.parseWithOptions(JustifyContent, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"justify-content" = .{ c, pre } } }; + } + } + }, + .@"place-content" => { + if (css.generic.parseWithOptions(PlaceContent, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"place-content" = c } }; + } + } + }, + .@"align-self" => |pre| { + if (css.generic.parseWithOptions(AlignSelf, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"align-self" = .{ c, pre } } }; + } + } + }, + .@"justify-self" => { + if (css.generic.parseWithOptions(JustifySelf, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"justify-self" = c } }; + } + } + }, + .@"place-self" => { + if (css.generic.parseWithOptions(PlaceSelf, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"place-self" = c } }; + } + } + }, + .@"align-items" => |pre| { + if (css.generic.parseWithOptions(AlignItems, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"align-items" = .{ c, pre } } }; + } + } + }, + .@"justify-items" => { + if (css.generic.parseWithOptions(JustifyItems, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"justify-items" = c } }; + } + } + }, + .@"place-items" => { + if (css.generic.parseWithOptions(PlaceItems, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"place-items" = c } }; + } + } + }, + .@"row-gap" => { + if (css.generic.parseWithOptions(GapValue, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"row-gap" = c } }; + } + } + }, + .@"column-gap" => { + if (css.generic.parseWithOptions(GapValue, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"column-gap" = c } }; + } + } + }, + .gap => { + if (css.generic.parseWithOptions(Gap, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .gap = c } }; + } + } + }, + .@"box-orient" => |pre| { + if (css.generic.parseWithOptions(BoxOrient, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"box-orient" = .{ c, pre } } }; + } + } + }, + .@"box-direction" => |pre| { + if (css.generic.parseWithOptions(BoxDirection, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"box-direction" = .{ c, pre } } }; + } + } + }, + .@"box-ordinal-group" => |pre| { + if (css.generic.parseWithOptions(CSSInteger, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"box-ordinal-group" = .{ c, pre } } }; + } + } + }, + .@"box-align" => |pre| { + if (css.generic.parseWithOptions(BoxAlign, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"box-align" = .{ c, pre } } }; + } + } + }, + .@"box-flex" => |pre| { + if (css.generic.parseWithOptions(CSSNumber, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"box-flex" = .{ c, pre } } }; + } + } + }, + .@"box-flex-group" => |pre| { + if (css.generic.parseWithOptions(CSSInteger, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"box-flex-group" = .{ c, pre } } }; + } + } + }, + .@"box-pack" => |pre| { + if (css.generic.parseWithOptions(BoxPack, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"box-pack" = .{ c, pre } } }; + } + } + }, + .@"box-lines" => |pre| { + if (css.generic.parseWithOptions(BoxLines, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"box-lines" = .{ c, pre } } }; + } + } + }, + .@"flex-pack" => |pre| { + if (css.generic.parseWithOptions(FlexPack, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"flex-pack" = .{ c, pre } } }; + } + } + }, + .@"flex-order" => |pre| { + if (css.generic.parseWithOptions(CSSInteger, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"flex-order" = .{ c, pre } } }; + } + } + }, + .@"flex-align" => |pre| { + if (css.generic.parseWithOptions(BoxAlign, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"flex-align" = .{ c, pre } } }; + } + } + }, + .@"flex-item-align" => |pre| { + if (css.generic.parseWithOptions(FlexItemAlign, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"flex-item-align" = .{ c, pre } } }; + } + } + }, + .@"flex-line-pack" => |pre| { + if (css.generic.parseWithOptions(FlexLinePack, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"flex-line-pack" = .{ c, pre } } }; + } + } + }, + .@"flex-positive" => |pre| { + if (css.generic.parseWithOptions(CSSNumber, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"flex-positive" = .{ c, pre } } }; + } + } + }, + .@"flex-negative" => |pre| { + if (css.generic.parseWithOptions(CSSNumber, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"flex-negative" = .{ c, pre } } }; + } + } + }, + .@"flex-preferred-size" => |pre| { + if (css.generic.parseWithOptions(LengthPercentageOrAuto, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"flex-preferred-size" = .{ c, pre } } }; + } + } + }, + .@"margin-top" => { + if (css.generic.parseWithOptions(LengthPercentageOrAuto, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"margin-top" = c } }; + } + } + }, + .@"margin-bottom" => { + if (css.generic.parseWithOptions(LengthPercentageOrAuto, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"margin-bottom" = c } }; + } + } + }, + .@"margin-left" => { + if (css.generic.parseWithOptions(LengthPercentageOrAuto, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"margin-left" = c } }; + } + } + }, + .@"margin-right" => { + if (css.generic.parseWithOptions(LengthPercentageOrAuto, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"margin-right" = c } }; + } + } + }, + .@"margin-block-start" => { + if (css.generic.parseWithOptions(LengthPercentageOrAuto, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"margin-block-start" = c } }; + } + } + }, + .@"margin-block-end" => { + if (css.generic.parseWithOptions(LengthPercentageOrAuto, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"margin-block-end" = c } }; + } + } + }, + .@"margin-inline-start" => { + if (css.generic.parseWithOptions(LengthPercentageOrAuto, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"margin-inline-start" = c } }; + } + } + }, + .@"margin-inline-end" => { + if (css.generic.parseWithOptions(LengthPercentageOrAuto, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"margin-inline-end" = c } }; + } + } + }, + .@"margin-block" => { + if (css.generic.parseWithOptions(MarginBlock, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"margin-block" = c } }; + } + } + }, + .@"margin-inline" => { + if (css.generic.parseWithOptions(MarginInline, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"margin-inline" = c } }; + } + } + }, + .margin => { + @setEvalBranchQuota(5000); + if (css.generic.parseWithOptions(Margin, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .margin = c } }; + } + } + }, + .@"padding-top" => { + if (css.generic.parseWithOptions(LengthPercentageOrAuto, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"padding-top" = c } }; + } + } + }, + .@"padding-bottom" => { + if (css.generic.parseWithOptions(LengthPercentageOrAuto, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"padding-bottom" = c } }; + } + } + }, + .@"padding-left" => { + if (css.generic.parseWithOptions(LengthPercentageOrAuto, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"padding-left" = c } }; + } + } + }, + .@"padding-right" => { + if (css.generic.parseWithOptions(LengthPercentageOrAuto, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"padding-right" = c } }; + } + } + }, + .@"padding-block-start" => { + if (css.generic.parseWithOptions(LengthPercentageOrAuto, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"padding-block-start" = c } }; + } + } + }, + .@"padding-block-end" => { + if (css.generic.parseWithOptions(LengthPercentageOrAuto, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"padding-block-end" = c } }; + } + } + }, + .@"padding-inline-start" => { + if (css.generic.parseWithOptions(LengthPercentageOrAuto, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"padding-inline-start" = c } }; + } + } + }, + .@"padding-inline-end" => { + if (css.generic.parseWithOptions(LengthPercentageOrAuto, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"padding-inline-end" = c } }; + } + } + }, + .@"padding-block" => { + if (css.generic.parseWithOptions(PaddingBlock, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"padding-block" = c } }; + } + } + }, + .@"padding-inline" => { + if (css.generic.parseWithOptions(PaddingInline, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"padding-inline" = c } }; + } + } + }, + .padding => { + if (css.generic.parseWithOptions(Padding, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .padding = c } }; + } + } + }, + .@"scroll-margin-top" => { + if (css.generic.parseWithOptions(LengthPercentageOrAuto, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"scroll-margin-top" = c } }; + } + } + }, + .@"scroll-margin-bottom" => { + if (css.generic.parseWithOptions(LengthPercentageOrAuto, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"scroll-margin-bottom" = c } }; + } + } + }, + .@"scroll-margin-left" => { + if (css.generic.parseWithOptions(LengthPercentageOrAuto, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"scroll-margin-left" = c } }; + } + } + }, + .@"scroll-margin-right" => { + if (css.generic.parseWithOptions(LengthPercentageOrAuto, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"scroll-margin-right" = c } }; + } + } + }, + .@"scroll-margin-block-start" => { + if (css.generic.parseWithOptions(LengthPercentageOrAuto, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"scroll-margin-block-start" = c } }; + } + } + }, + .@"scroll-margin-block-end" => { + if (css.generic.parseWithOptions(LengthPercentageOrAuto, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"scroll-margin-block-end" = c } }; + } + } + }, + .@"scroll-margin-inline-start" => { + if (css.generic.parseWithOptions(LengthPercentageOrAuto, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"scroll-margin-inline-start" = c } }; + } + } + }, + .@"scroll-margin-inline-end" => { + if (css.generic.parseWithOptions(LengthPercentageOrAuto, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"scroll-margin-inline-end" = c } }; + } + } + }, + .@"scroll-margin-block" => { + if (css.generic.parseWithOptions(ScrollMarginBlock, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"scroll-margin-block" = c } }; + } + } + }, + .@"scroll-margin-inline" => { + if (css.generic.parseWithOptions(ScrollMarginInline, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"scroll-margin-inline" = c } }; + } + } + }, + .@"scroll-margin" => { + if (css.generic.parseWithOptions(ScrollMargin, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"scroll-margin" = c } }; + } + } + }, + .@"scroll-padding-top" => { + if (css.generic.parseWithOptions(LengthPercentageOrAuto, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"scroll-padding-top" = c } }; + } + } + }, + .@"scroll-padding-bottom" => { + if (css.generic.parseWithOptions(LengthPercentageOrAuto, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"scroll-padding-bottom" = c } }; + } + } + }, + .@"scroll-padding-left" => { + if (css.generic.parseWithOptions(LengthPercentageOrAuto, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"scroll-padding-left" = c } }; + } + } + }, + .@"scroll-padding-right" => { + if (css.generic.parseWithOptions(LengthPercentageOrAuto, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"scroll-padding-right" = c } }; + } + } + }, + .@"scroll-padding-block-start" => { + if (css.generic.parseWithOptions(LengthPercentageOrAuto, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"scroll-padding-block-start" = c } }; + } + } + }, + .@"scroll-padding-block-end" => { + if (css.generic.parseWithOptions(LengthPercentageOrAuto, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"scroll-padding-block-end" = c } }; + } + } + }, + .@"scroll-padding-inline-start" => { + if (css.generic.parseWithOptions(LengthPercentageOrAuto, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"scroll-padding-inline-start" = c } }; + } + } + }, + .@"scroll-padding-inline-end" => { + if (css.generic.parseWithOptions(LengthPercentageOrAuto, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"scroll-padding-inline-end" = c } }; + } + } + }, + .@"scroll-padding-block" => { + if (css.generic.parseWithOptions(ScrollPaddingBlock, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"scroll-padding-block" = c } }; + } + } + }, + .@"scroll-padding-inline" => { + if (css.generic.parseWithOptions(ScrollPaddingInline, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"scroll-padding-inline" = c } }; + } + } + }, + .@"scroll-padding" => { + if (css.generic.parseWithOptions(ScrollPadding, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"scroll-padding" = c } }; + } + } + }, + .@"font-weight" => { + if (css.generic.parseWithOptions(FontWeight, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"font-weight" = c } }; + } + } + }, + .@"font-size" => { + if (css.generic.parseWithOptions(FontSize, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"font-size" = c } }; + } + } + }, + .@"font-stretch" => { + if (css.generic.parseWithOptions(FontStretch, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"font-stretch" = c } }; + } + } + }, + .@"font-family" => { + if (css.generic.parseWithOptions(BabyList(FontFamily), input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"font-family" = c } }; + } + } + }, + .@"font-style" => { + if (css.generic.parseWithOptions(FontStyle, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"font-style" = c } }; + } + } + }, + .@"font-variant-caps" => { + if (css.generic.parseWithOptions(FontVariantCaps, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"font-variant-caps" = c } }; + } + } + }, + .@"line-height" => { + if (css.generic.parseWithOptions(LineHeight, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"line-height" = c } }; + } + } + }, + .font => { + if (css.generic.parseWithOptions(Font, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .font = c } }; + } + } + }, .@"text-decoration-color" => |pre| { if (css.generic.parseWithOptions(CssColor, input, options).asValue()) |c| { if (input.expectExhausted().isOk()) { @@ -445,6 +5426,13 @@ pub const Property = union(PropertyIdTag) { } } }, + .direction => { + if (css.generic.parseWithOptions(Direction, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .direction = c } }; + } + } + }, .composes => { if (css.generic.parseWithOptions(Composes, input, options).asValue()) |c| { if (input.expectExhausted().isOk()) { @@ -452,6 +5440,196 @@ pub const Property = union(PropertyIdTag) { } } }, + .@"mask-image" => |pre| { + if (css.generic.parseWithOptions(SmallList(Image, 1), input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"mask-image" = .{ c, pre } } }; + } + } + }, + .@"mask-mode" => { + if (css.generic.parseWithOptions(SmallList(MaskMode, 1), input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"mask-mode" = c } }; + } + } + }, + .@"mask-repeat" => |pre| { + if (css.generic.parseWithOptions(SmallList(BackgroundRepeat, 1), input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"mask-repeat" = .{ c, pre } } }; + } + } + }, + .@"mask-position-x" => { + if (css.generic.parseWithOptions(SmallList(HorizontalPosition, 1), input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"mask-position-x" = c } }; + } + } + }, + .@"mask-position-y" => { + if (css.generic.parseWithOptions(SmallList(VerticalPosition, 1), input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"mask-position-y" = c } }; + } + } + }, + .@"mask-position" => |pre| { + if (css.generic.parseWithOptions(SmallList(Position, 1), input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"mask-position" = .{ c, pre } } }; + } + } + }, + .@"mask-clip" => |pre| { + @setEvalBranchQuota(5000); + if (css.generic.parseWithOptions(SmallList(MaskClip, 1), input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"mask-clip" = .{ c, pre } } }; + } + } + }, + .@"mask-origin" => |pre| { + if (css.generic.parseWithOptions(SmallList(GeometryBox, 1), input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"mask-origin" = .{ c, pre } } }; + } + } + }, + .@"mask-size" => |pre| { + if (css.generic.parseWithOptions(SmallList(BackgroundSize, 1), input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"mask-size" = .{ c, pre } } }; + } + } + }, + .@"mask-composite" => { + if (css.generic.parseWithOptions(SmallList(MaskComposite, 1), input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"mask-composite" = c } }; + } + } + }, + .@"mask-type" => { + if (css.generic.parseWithOptions(MaskType, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"mask-type" = c } }; + } + } + }, + .mask => |pre| { + if (css.generic.parseWithOptions(SmallList(Mask, 1), input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .mask = .{ c, pre } } }; + } + } + }, + .@"mask-border-source" => { + if (css.generic.parseWithOptions(Image, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"mask-border-source" = c } }; + } + } + }, + .@"mask-border-mode" => { + if (css.generic.parseWithOptions(MaskBorderMode, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"mask-border-mode" = c } }; + } + } + }, + .@"mask-border-slice" => { + if (css.generic.parseWithOptions(BorderImageSlice, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"mask-border-slice" = c } }; + } + } + }, + .@"mask-border-width" => { + if (css.generic.parseWithOptions(Rect(BorderImageSideWidth), input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"mask-border-width" = c } }; + } + } + }, + .@"mask-border-outset" => { + if (css.generic.parseWithOptions(Rect(LengthOrNumber), input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"mask-border-outset" = c } }; + } + } + }, + .@"mask-border-repeat" => { + if (css.generic.parseWithOptions(BorderImageRepeat, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"mask-border-repeat" = c } }; + } + } + }, + .@"mask-border" => { + if (css.generic.parseWithOptions(MaskBorder, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"mask-border" = c } }; + } + } + }, + .@"-webkit-mask-composite" => { + if (css.generic.parseWithOptions(SmallList(WebKitMaskComposite, 1), input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"-webkit-mask-composite" = c } }; + } + } + }, + .@"mask-source-type" => |pre| { + if (css.generic.parseWithOptions(SmallList(WebKitMaskSourceType, 1), input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"mask-source-type" = .{ c, pre } } }; + } + } + }, + .@"mask-box-image" => |pre| { + if (css.generic.parseWithOptions(BorderImage, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"mask-box-image" = .{ c, pre } } }; + } + } + }, + .@"mask-box-image-source" => |pre| { + if (css.generic.parseWithOptions(Image, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"mask-box-image-source" = .{ c, pre } } }; + } + } + }, + .@"mask-box-image-slice" => |pre| { + if (css.generic.parseWithOptions(BorderImageSlice, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"mask-box-image-slice" = .{ c, pre } } }; + } + } + }, + .@"mask-box-image-width" => |pre| { + if (css.generic.parseWithOptions(Rect(BorderImageSideWidth), input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"mask-box-image-width" = .{ c, pre } } }; + } + } + }, + .@"mask-box-image-outset" => |pre| { + if (css.generic.parseWithOptions(Rect(LengthOrNumber), input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"mask-box-image-outset" = .{ c, pre } } }; + } + } + }, + .@"mask-box-image-repeat" => |pre| { + if (css.generic.parseWithOptions(BorderImageRepeat, input, options).asValue()) |c| { + if (input.expectExhausted().isOk()) { + return .{ .result = .{ .@"mask-box-image-repeat" = .{ c, pre } } }; + } + } + }, .all => return .{ .result = .{ .all = switch (CSSWideKeyword.parse(input)) { .result => |v| v, .err => |e| return .{ .err = e }, @@ -474,10 +5652,538 @@ pub const Property = union(PropertyIdTag) { } } }; } + pub fn propertyId(this: *const Property) PropertyId { + return switch (this.*) { + .@"background-color" => .@"background-color", + .@"background-image" => .@"background-image", + .@"background-position-x" => .@"background-position-x", + .@"background-position-y" => .@"background-position-y", + .@"background-position" => .@"background-position", + .@"background-size" => .@"background-size", + .@"background-repeat" => .@"background-repeat", + .@"background-attachment" => .@"background-attachment", + .@"background-clip" => |*v| PropertyId{ .@"background-clip" = v[1] }, + .@"background-origin" => .@"background-origin", + .background => .background, + .@"box-shadow" => |*v| PropertyId{ .@"box-shadow" = v[1] }, + .opacity => .opacity, + .color => .color, + .display => .display, + .visibility => .visibility, + .width => .width, + .height => .height, + .@"min-width" => .@"min-width", + .@"min-height" => .@"min-height", + .@"max-width" => .@"max-width", + .@"max-height" => .@"max-height", + .@"block-size" => .@"block-size", + .@"inline-size" => .@"inline-size", + .@"min-block-size" => .@"min-block-size", + .@"min-inline-size" => .@"min-inline-size", + .@"max-block-size" => .@"max-block-size", + .@"max-inline-size" => .@"max-inline-size", + .@"box-sizing" => |*v| PropertyId{ .@"box-sizing" = v[1] }, + .@"aspect-ratio" => .@"aspect-ratio", + .overflow => .overflow, + .@"overflow-x" => .@"overflow-x", + .@"overflow-y" => .@"overflow-y", + .@"text-overflow" => |*v| PropertyId{ .@"text-overflow" = v[1] }, + .position => .position, + .top => .top, + .bottom => .bottom, + .left => .left, + .right => .right, + .@"inset-block-start" => .@"inset-block-start", + .@"inset-block-end" => .@"inset-block-end", + .@"inset-inline-start" => .@"inset-inline-start", + .@"inset-inline-end" => .@"inset-inline-end", + .@"inset-block" => .@"inset-block", + .@"inset-inline" => .@"inset-inline", + .inset => .inset, + .@"border-spacing" => .@"border-spacing", + .@"border-top-color" => .@"border-top-color", + .@"border-bottom-color" => .@"border-bottom-color", + .@"border-left-color" => .@"border-left-color", + .@"border-right-color" => .@"border-right-color", + .@"border-block-start-color" => .@"border-block-start-color", + .@"border-block-end-color" => .@"border-block-end-color", + .@"border-inline-start-color" => .@"border-inline-start-color", + .@"border-inline-end-color" => .@"border-inline-end-color", + .@"border-top-style" => .@"border-top-style", + .@"border-bottom-style" => .@"border-bottom-style", + .@"border-left-style" => .@"border-left-style", + .@"border-right-style" => .@"border-right-style", + .@"border-block-start-style" => .@"border-block-start-style", + .@"border-block-end-style" => .@"border-block-end-style", + .@"border-inline-start-style" => .@"border-inline-start-style", + .@"border-inline-end-style" => .@"border-inline-end-style", + .@"border-top-width" => .@"border-top-width", + .@"border-bottom-width" => .@"border-bottom-width", + .@"border-left-width" => .@"border-left-width", + .@"border-right-width" => .@"border-right-width", + .@"border-block-start-width" => .@"border-block-start-width", + .@"border-block-end-width" => .@"border-block-end-width", + .@"border-inline-start-width" => .@"border-inline-start-width", + .@"border-inline-end-width" => .@"border-inline-end-width", + .@"border-top-left-radius" => |*v| PropertyId{ .@"border-top-left-radius" = v[1] }, + .@"border-top-right-radius" => |*v| PropertyId{ .@"border-top-right-radius" = v[1] }, + .@"border-bottom-left-radius" => |*v| PropertyId{ .@"border-bottom-left-radius" = v[1] }, + .@"border-bottom-right-radius" => |*v| PropertyId{ .@"border-bottom-right-radius" = v[1] }, + .@"border-start-start-radius" => .@"border-start-start-radius", + .@"border-start-end-radius" => .@"border-start-end-radius", + .@"border-end-start-radius" => .@"border-end-start-radius", + .@"border-end-end-radius" => .@"border-end-end-radius", + .@"border-radius" => |*v| PropertyId{ .@"border-radius" = v[1] }, + .@"border-image-source" => .@"border-image-source", + .@"border-image-outset" => .@"border-image-outset", + .@"border-image-repeat" => .@"border-image-repeat", + .@"border-image-width" => .@"border-image-width", + .@"border-image-slice" => .@"border-image-slice", + .@"border-image" => |*v| PropertyId{ .@"border-image" = v[1] }, + .@"border-color" => .@"border-color", + .@"border-style" => .@"border-style", + .@"border-width" => .@"border-width", + .@"border-block-color" => .@"border-block-color", + .@"border-block-style" => .@"border-block-style", + .@"border-block-width" => .@"border-block-width", + .@"border-inline-color" => .@"border-inline-color", + .@"border-inline-style" => .@"border-inline-style", + .@"border-inline-width" => .@"border-inline-width", + .border => .border, + .@"border-top" => .@"border-top", + .@"border-bottom" => .@"border-bottom", + .@"border-left" => .@"border-left", + .@"border-right" => .@"border-right", + .@"border-block" => .@"border-block", + .@"border-block-start" => .@"border-block-start", + .@"border-block-end" => .@"border-block-end", + .@"border-inline" => .@"border-inline", + .@"border-inline-start" => .@"border-inline-start", + .@"border-inline-end" => .@"border-inline-end", + .outline => .outline, + .@"outline-color" => .@"outline-color", + .@"outline-style" => .@"outline-style", + .@"outline-width" => .@"outline-width", + .@"flex-direction" => |*v| PropertyId{ .@"flex-direction" = v[1] }, + .@"flex-wrap" => |*v| PropertyId{ .@"flex-wrap" = v[1] }, + .@"flex-flow" => |*v| PropertyId{ .@"flex-flow" = v[1] }, + .@"flex-grow" => |*v| PropertyId{ .@"flex-grow" = v[1] }, + .@"flex-shrink" => |*v| PropertyId{ .@"flex-shrink" = v[1] }, + .@"flex-basis" => |*v| PropertyId{ .@"flex-basis" = v[1] }, + .flex => |*v| PropertyId{ .flex = v[1] }, + .order => |*v| PropertyId{ .order = v[1] }, + .@"align-content" => |*v| PropertyId{ .@"align-content" = v[1] }, + .@"justify-content" => |*v| PropertyId{ .@"justify-content" = v[1] }, + .@"place-content" => .@"place-content", + .@"align-self" => |*v| PropertyId{ .@"align-self" = v[1] }, + .@"justify-self" => .@"justify-self", + .@"place-self" => .@"place-self", + .@"align-items" => |*v| PropertyId{ .@"align-items" = v[1] }, + .@"justify-items" => .@"justify-items", + .@"place-items" => .@"place-items", + .@"row-gap" => .@"row-gap", + .@"column-gap" => .@"column-gap", + .gap => .gap, + .@"box-orient" => |*v| PropertyId{ .@"box-orient" = v[1] }, + .@"box-direction" => |*v| PropertyId{ .@"box-direction" = v[1] }, + .@"box-ordinal-group" => |*v| PropertyId{ .@"box-ordinal-group" = v[1] }, + .@"box-align" => |*v| PropertyId{ .@"box-align" = v[1] }, + .@"box-flex" => |*v| PropertyId{ .@"box-flex" = v[1] }, + .@"box-flex-group" => |*v| PropertyId{ .@"box-flex-group" = v[1] }, + .@"box-pack" => |*v| PropertyId{ .@"box-pack" = v[1] }, + .@"box-lines" => |*v| PropertyId{ .@"box-lines" = v[1] }, + .@"flex-pack" => |*v| PropertyId{ .@"flex-pack" = v[1] }, + .@"flex-order" => |*v| PropertyId{ .@"flex-order" = v[1] }, + .@"flex-align" => |*v| PropertyId{ .@"flex-align" = v[1] }, + .@"flex-item-align" => |*v| PropertyId{ .@"flex-item-align" = v[1] }, + .@"flex-line-pack" => |*v| PropertyId{ .@"flex-line-pack" = v[1] }, + .@"flex-positive" => |*v| PropertyId{ .@"flex-positive" = v[1] }, + .@"flex-negative" => |*v| PropertyId{ .@"flex-negative" = v[1] }, + .@"flex-preferred-size" => |*v| PropertyId{ .@"flex-preferred-size" = v[1] }, + .@"margin-top" => .@"margin-top", + .@"margin-bottom" => .@"margin-bottom", + .@"margin-left" => .@"margin-left", + .@"margin-right" => .@"margin-right", + .@"margin-block-start" => .@"margin-block-start", + .@"margin-block-end" => .@"margin-block-end", + .@"margin-inline-start" => .@"margin-inline-start", + .@"margin-inline-end" => .@"margin-inline-end", + .@"margin-block" => .@"margin-block", + .@"margin-inline" => .@"margin-inline", + .margin => .margin, + .@"padding-top" => .@"padding-top", + .@"padding-bottom" => .@"padding-bottom", + .@"padding-left" => .@"padding-left", + .@"padding-right" => .@"padding-right", + .@"padding-block-start" => .@"padding-block-start", + .@"padding-block-end" => .@"padding-block-end", + .@"padding-inline-start" => .@"padding-inline-start", + .@"padding-inline-end" => .@"padding-inline-end", + .@"padding-block" => .@"padding-block", + .@"padding-inline" => .@"padding-inline", + .padding => .padding, + .@"scroll-margin-top" => .@"scroll-margin-top", + .@"scroll-margin-bottom" => .@"scroll-margin-bottom", + .@"scroll-margin-left" => .@"scroll-margin-left", + .@"scroll-margin-right" => .@"scroll-margin-right", + .@"scroll-margin-block-start" => .@"scroll-margin-block-start", + .@"scroll-margin-block-end" => .@"scroll-margin-block-end", + .@"scroll-margin-inline-start" => .@"scroll-margin-inline-start", + .@"scroll-margin-inline-end" => .@"scroll-margin-inline-end", + .@"scroll-margin-block" => .@"scroll-margin-block", + .@"scroll-margin-inline" => .@"scroll-margin-inline", + .@"scroll-margin" => .@"scroll-margin", + .@"scroll-padding-top" => .@"scroll-padding-top", + .@"scroll-padding-bottom" => .@"scroll-padding-bottom", + .@"scroll-padding-left" => .@"scroll-padding-left", + .@"scroll-padding-right" => .@"scroll-padding-right", + .@"scroll-padding-block-start" => .@"scroll-padding-block-start", + .@"scroll-padding-block-end" => .@"scroll-padding-block-end", + .@"scroll-padding-inline-start" => .@"scroll-padding-inline-start", + .@"scroll-padding-inline-end" => .@"scroll-padding-inline-end", + .@"scroll-padding-block" => .@"scroll-padding-block", + .@"scroll-padding-inline" => .@"scroll-padding-inline", + .@"scroll-padding" => .@"scroll-padding", + .@"font-weight" => .@"font-weight", + .@"font-size" => .@"font-size", + .@"font-stretch" => .@"font-stretch", + .@"font-family" => .@"font-family", + .@"font-style" => .@"font-style", + .@"font-variant-caps" => .@"font-variant-caps", + .@"line-height" => .@"line-height", + .font => .font, + .@"text-decoration-color" => |*v| PropertyId{ .@"text-decoration-color" = v[1] }, + .@"text-emphasis-color" => |*v| PropertyId{ .@"text-emphasis-color" = v[1] }, + .direction => .direction, + .composes => .composes, + .@"mask-image" => |*v| PropertyId{ .@"mask-image" = v[1] }, + .@"mask-mode" => .@"mask-mode", + .@"mask-repeat" => |*v| PropertyId{ .@"mask-repeat" = v[1] }, + .@"mask-position-x" => .@"mask-position-x", + .@"mask-position-y" => .@"mask-position-y", + .@"mask-position" => |*v| PropertyId{ .@"mask-position" = v[1] }, + .@"mask-clip" => |*v| PropertyId{ .@"mask-clip" = v[1] }, + .@"mask-origin" => |*v| PropertyId{ .@"mask-origin" = v[1] }, + .@"mask-size" => |*v| PropertyId{ .@"mask-size" = v[1] }, + .@"mask-composite" => .@"mask-composite", + .@"mask-type" => .@"mask-type", + .mask => |*v| PropertyId{ .mask = v[1] }, + .@"mask-border-source" => .@"mask-border-source", + .@"mask-border-mode" => .@"mask-border-mode", + .@"mask-border-slice" => .@"mask-border-slice", + .@"mask-border-width" => .@"mask-border-width", + .@"mask-border-outset" => .@"mask-border-outset", + .@"mask-border-repeat" => .@"mask-border-repeat", + .@"mask-border" => .@"mask-border", + .@"-webkit-mask-composite" => .@"-webkit-mask-composite", + .@"mask-source-type" => |*v| PropertyId{ .@"mask-source-type" = v[1] }, + .@"mask-box-image" => |*v| PropertyId{ .@"mask-box-image" = v[1] }, + .@"mask-box-image-source" => |*v| PropertyId{ .@"mask-box-image-source" = v[1] }, + .@"mask-box-image-slice" => |*v| PropertyId{ .@"mask-box-image-slice" = v[1] }, + .@"mask-box-image-width" => |*v| PropertyId{ .@"mask-box-image-width" = v[1] }, + .@"mask-box-image-outset" => |*v| PropertyId{ .@"mask-box-image-outset" = v[1] }, + .@"mask-box-image-repeat" => |*v| PropertyId{ .@"mask-box-image-repeat" = v[1] }, + .all => PropertyId.all, + .unparsed => |unparsed| unparsed.property_id, + .custom => |c| .{ .custom = c.name }, + }; + } + + pub fn deepClone(this: *const Property, allocator: std.mem.Allocator) Property { + return switch (this.*) { + .@"background-color" => |*v| .{ .@"background-color" = v.deepClone(allocator) }, + .@"background-image" => |*v| .{ .@"background-image" = v.deepClone(allocator) }, + .@"background-position-x" => |*v| .{ .@"background-position-x" = v.deepClone(allocator) }, + .@"background-position-y" => |*v| .{ .@"background-position-y" = v.deepClone(allocator) }, + .@"background-position" => |*v| .{ .@"background-position" = v.deepClone(allocator) }, + .@"background-size" => |*v| .{ .@"background-size" = v.deepClone(allocator) }, + .@"background-repeat" => |*v| .{ .@"background-repeat" = v.deepClone(allocator) }, + .@"background-attachment" => |*v| .{ .@"background-attachment" = v.deepClone(allocator) }, + .@"background-clip" => |*v| .{ .@"background-clip" = .{ v[0].deepClone(allocator), v[1] } }, + .@"background-origin" => |*v| .{ .@"background-origin" = v.deepClone(allocator) }, + .background => |*v| .{ .background = v.deepClone(allocator) }, + .@"box-shadow" => |*v| .{ .@"box-shadow" = .{ v[0].deepClone(allocator), v[1] } }, + .opacity => |*v| .{ .opacity = v.deepClone(allocator) }, + .color => |*v| .{ .color = v.deepClone(allocator) }, + .display => |*v| .{ .display = v.deepClone(allocator) }, + .visibility => |*v| .{ .visibility = v.deepClone(allocator) }, + .width => |*v| .{ .width = v.deepClone(allocator) }, + .height => |*v| .{ .height = v.deepClone(allocator) }, + .@"min-width" => |*v| .{ .@"min-width" = v.deepClone(allocator) }, + .@"min-height" => |*v| .{ .@"min-height" = v.deepClone(allocator) }, + .@"max-width" => |*v| .{ .@"max-width" = v.deepClone(allocator) }, + .@"max-height" => |*v| .{ .@"max-height" = v.deepClone(allocator) }, + .@"block-size" => |*v| .{ .@"block-size" = v.deepClone(allocator) }, + .@"inline-size" => |*v| .{ .@"inline-size" = v.deepClone(allocator) }, + .@"min-block-size" => |*v| .{ .@"min-block-size" = v.deepClone(allocator) }, + .@"min-inline-size" => |*v| .{ .@"min-inline-size" = v.deepClone(allocator) }, + .@"max-block-size" => |*v| .{ .@"max-block-size" = v.deepClone(allocator) }, + .@"max-inline-size" => |*v| .{ .@"max-inline-size" = v.deepClone(allocator) }, + .@"box-sizing" => |*v| .{ .@"box-sizing" = .{ v[0].deepClone(allocator), v[1] } }, + .@"aspect-ratio" => |*v| .{ .@"aspect-ratio" = v.deepClone(allocator) }, + .overflow => |*v| .{ .overflow = v.deepClone(allocator) }, + .@"overflow-x" => |*v| .{ .@"overflow-x" = v.deepClone(allocator) }, + .@"overflow-y" => |*v| .{ .@"overflow-y" = v.deepClone(allocator) }, + .@"text-overflow" => |*v| .{ .@"text-overflow" = .{ v[0].deepClone(allocator), v[1] } }, + .position => |*v| .{ .position = v.deepClone(allocator) }, + .top => |*v| .{ .top = v.deepClone(allocator) }, + .bottom => |*v| .{ .bottom = v.deepClone(allocator) }, + .left => |*v| .{ .left = v.deepClone(allocator) }, + .right => |*v| .{ .right = v.deepClone(allocator) }, + .@"inset-block-start" => |*v| .{ .@"inset-block-start" = v.deepClone(allocator) }, + .@"inset-block-end" => |*v| .{ .@"inset-block-end" = v.deepClone(allocator) }, + .@"inset-inline-start" => |*v| .{ .@"inset-inline-start" = v.deepClone(allocator) }, + .@"inset-inline-end" => |*v| .{ .@"inset-inline-end" = v.deepClone(allocator) }, + .@"inset-block" => |*v| .{ .@"inset-block" = v.deepClone(allocator) }, + .@"inset-inline" => |*v| .{ .@"inset-inline" = v.deepClone(allocator) }, + .inset => |*v| .{ .inset = v.deepClone(allocator) }, + .@"border-spacing" => |*v| .{ .@"border-spacing" = v.deepClone(allocator) }, + .@"border-top-color" => |*v| .{ .@"border-top-color" = v.deepClone(allocator) }, + .@"border-bottom-color" => |*v| .{ .@"border-bottom-color" = v.deepClone(allocator) }, + .@"border-left-color" => |*v| .{ .@"border-left-color" = v.deepClone(allocator) }, + .@"border-right-color" => |*v| .{ .@"border-right-color" = v.deepClone(allocator) }, + .@"border-block-start-color" => |*v| .{ .@"border-block-start-color" = v.deepClone(allocator) }, + .@"border-block-end-color" => |*v| .{ .@"border-block-end-color" = v.deepClone(allocator) }, + .@"border-inline-start-color" => |*v| .{ .@"border-inline-start-color" = v.deepClone(allocator) }, + .@"border-inline-end-color" => |*v| .{ .@"border-inline-end-color" = v.deepClone(allocator) }, + .@"border-top-style" => |*v| .{ .@"border-top-style" = v.deepClone(allocator) }, + .@"border-bottom-style" => |*v| .{ .@"border-bottom-style" = v.deepClone(allocator) }, + .@"border-left-style" => |*v| .{ .@"border-left-style" = v.deepClone(allocator) }, + .@"border-right-style" => |*v| .{ .@"border-right-style" = v.deepClone(allocator) }, + .@"border-block-start-style" => |*v| .{ .@"border-block-start-style" = v.deepClone(allocator) }, + .@"border-block-end-style" => |*v| .{ .@"border-block-end-style" = v.deepClone(allocator) }, + .@"border-inline-start-style" => |*v| .{ .@"border-inline-start-style" = v.deepClone(allocator) }, + .@"border-inline-end-style" => |*v| .{ .@"border-inline-end-style" = v.deepClone(allocator) }, + .@"border-top-width" => |*v| .{ .@"border-top-width" = v.deepClone(allocator) }, + .@"border-bottom-width" => |*v| .{ .@"border-bottom-width" = v.deepClone(allocator) }, + .@"border-left-width" => |*v| .{ .@"border-left-width" = v.deepClone(allocator) }, + .@"border-right-width" => |*v| .{ .@"border-right-width" = v.deepClone(allocator) }, + .@"border-block-start-width" => |*v| .{ .@"border-block-start-width" = v.deepClone(allocator) }, + .@"border-block-end-width" => |*v| .{ .@"border-block-end-width" = v.deepClone(allocator) }, + .@"border-inline-start-width" => |*v| .{ .@"border-inline-start-width" = v.deepClone(allocator) }, + .@"border-inline-end-width" => |*v| .{ .@"border-inline-end-width" = v.deepClone(allocator) }, + .@"border-top-left-radius" => |*v| .{ .@"border-top-left-radius" = .{ v[0].deepClone(allocator), v[1] } }, + .@"border-top-right-radius" => |*v| .{ .@"border-top-right-radius" = .{ v[0].deepClone(allocator), v[1] } }, + .@"border-bottom-left-radius" => |*v| .{ .@"border-bottom-left-radius" = .{ v[0].deepClone(allocator), v[1] } }, + .@"border-bottom-right-radius" => |*v| .{ .@"border-bottom-right-radius" = .{ v[0].deepClone(allocator), v[1] } }, + .@"border-start-start-radius" => |*v| .{ .@"border-start-start-radius" = v.deepClone(allocator) }, + .@"border-start-end-radius" => |*v| .{ .@"border-start-end-radius" = v.deepClone(allocator) }, + .@"border-end-start-radius" => |*v| .{ .@"border-end-start-radius" = v.deepClone(allocator) }, + .@"border-end-end-radius" => |*v| .{ .@"border-end-end-radius" = v.deepClone(allocator) }, + .@"border-radius" => |*v| .{ .@"border-radius" = .{ v[0].deepClone(allocator), v[1] } }, + .@"border-image-source" => |*v| .{ .@"border-image-source" = v.deepClone(allocator) }, + .@"border-image-outset" => |*v| .{ .@"border-image-outset" = v.deepClone(allocator) }, + .@"border-image-repeat" => |*v| .{ .@"border-image-repeat" = v.deepClone(allocator) }, + .@"border-image-width" => |*v| .{ .@"border-image-width" = v.deepClone(allocator) }, + .@"border-image-slice" => |*v| .{ .@"border-image-slice" = v.deepClone(allocator) }, + .@"border-image" => |*v| .{ .@"border-image" = .{ v[0].deepClone(allocator), v[1] } }, + .@"border-color" => |*v| .{ .@"border-color" = v.deepClone(allocator) }, + .@"border-style" => |*v| .{ .@"border-style" = v.deepClone(allocator) }, + .@"border-width" => |*v| .{ .@"border-width" = v.deepClone(allocator) }, + .@"border-block-color" => |*v| .{ .@"border-block-color" = v.deepClone(allocator) }, + .@"border-block-style" => |*v| .{ .@"border-block-style" = v.deepClone(allocator) }, + .@"border-block-width" => |*v| .{ .@"border-block-width" = v.deepClone(allocator) }, + .@"border-inline-color" => |*v| .{ .@"border-inline-color" = v.deepClone(allocator) }, + .@"border-inline-style" => |*v| .{ .@"border-inline-style" = v.deepClone(allocator) }, + .@"border-inline-width" => |*v| .{ .@"border-inline-width" = v.deepClone(allocator) }, + .border => |*v| .{ .border = v.deepClone(allocator) }, + .@"border-top" => |*v| .{ .@"border-top" = v.deepClone(allocator) }, + .@"border-bottom" => |*v| .{ .@"border-bottom" = v.deepClone(allocator) }, + .@"border-left" => |*v| .{ .@"border-left" = v.deepClone(allocator) }, + .@"border-right" => |*v| .{ .@"border-right" = v.deepClone(allocator) }, + .@"border-block" => |*v| .{ .@"border-block" = v.deepClone(allocator) }, + .@"border-block-start" => |*v| .{ .@"border-block-start" = v.deepClone(allocator) }, + .@"border-block-end" => |*v| .{ .@"border-block-end" = v.deepClone(allocator) }, + .@"border-inline" => |*v| .{ .@"border-inline" = v.deepClone(allocator) }, + .@"border-inline-start" => |*v| .{ .@"border-inline-start" = v.deepClone(allocator) }, + .@"border-inline-end" => |*v| .{ .@"border-inline-end" = v.deepClone(allocator) }, + .outline => |*v| .{ .outline = v.deepClone(allocator) }, + .@"outline-color" => |*v| .{ .@"outline-color" = v.deepClone(allocator) }, + .@"outline-style" => |*v| .{ .@"outline-style" = v.deepClone(allocator) }, + .@"outline-width" => |*v| .{ .@"outline-width" = v.deepClone(allocator) }, + .@"flex-direction" => |*v| .{ .@"flex-direction" = .{ v[0].deepClone(allocator), v[1] } }, + .@"flex-wrap" => |*v| .{ .@"flex-wrap" = .{ v[0].deepClone(allocator), v[1] } }, + .@"flex-flow" => |*v| .{ .@"flex-flow" = .{ v[0].deepClone(allocator), v[1] } }, + .@"flex-grow" => |*v| .{ .@"flex-grow" = .{ v[0], v[1] } }, + .@"flex-shrink" => |*v| .{ .@"flex-shrink" = .{ v[0], v[1] } }, + .@"flex-basis" => |*v| .{ .@"flex-basis" = .{ v[0].deepClone(allocator), v[1] } }, + .flex => |*v| .{ .flex = .{ v[0].deepClone(allocator), v[1] } }, + .order => |*v| .{ .order = .{ v[0], v[1] } }, + .@"align-content" => |*v| .{ .@"align-content" = .{ v[0].deepClone(allocator), v[1] } }, + .@"justify-content" => |*v| .{ .@"justify-content" = .{ v[0].deepClone(allocator), v[1] } }, + .@"place-content" => |*v| .{ .@"place-content" = v.deepClone(allocator) }, + .@"align-self" => |*v| .{ .@"align-self" = .{ v[0].deepClone(allocator), v[1] } }, + .@"justify-self" => |*v| .{ .@"justify-self" = v.deepClone(allocator) }, + .@"place-self" => |*v| .{ .@"place-self" = v.deepClone(allocator) }, + .@"align-items" => |*v| .{ .@"align-items" = .{ v[0].deepClone(allocator), v[1] } }, + .@"justify-items" => |*v| .{ .@"justify-items" = v.deepClone(allocator) }, + .@"place-items" => |*v| .{ .@"place-items" = v.deepClone(allocator) }, + .@"row-gap" => |*v| .{ .@"row-gap" = v.deepClone(allocator) }, + .@"column-gap" => |*v| .{ .@"column-gap" = v.deepClone(allocator) }, + .gap => |*v| .{ .gap = v.deepClone(allocator) }, + .@"box-orient" => |*v| .{ .@"box-orient" = .{ v[0].deepClone(allocator), v[1] } }, + .@"box-direction" => |*v| .{ .@"box-direction" = .{ v[0].deepClone(allocator), v[1] } }, + .@"box-ordinal-group" => |*v| .{ .@"box-ordinal-group" = .{ v[0], v[1] } }, + .@"box-align" => |*v| .{ .@"box-align" = .{ v[0].deepClone(allocator), v[1] } }, + .@"box-flex" => |*v| .{ .@"box-flex" = .{ v[0], v[1] } }, + .@"box-flex-group" => |*v| .{ .@"box-flex-group" = .{ v[0], v[1] } }, + .@"box-pack" => |*v| .{ .@"box-pack" = .{ v[0].deepClone(allocator), v[1] } }, + .@"box-lines" => |*v| .{ .@"box-lines" = .{ v[0].deepClone(allocator), v[1] } }, + .@"flex-pack" => |*v| .{ .@"flex-pack" = .{ v[0].deepClone(allocator), v[1] } }, + .@"flex-order" => |*v| .{ .@"flex-order" = .{ v[0], v[1] } }, + .@"flex-align" => |*v| .{ .@"flex-align" = .{ v[0].deepClone(allocator), v[1] } }, + .@"flex-item-align" => |*v| .{ .@"flex-item-align" = .{ v[0].deepClone(allocator), v[1] } }, + .@"flex-line-pack" => |*v| .{ .@"flex-line-pack" = .{ v[0].deepClone(allocator), v[1] } }, + .@"flex-positive" => |*v| .{ .@"flex-positive" = .{ v[0], v[1] } }, + .@"flex-negative" => |*v| .{ .@"flex-negative" = .{ v[0], v[1] } }, + .@"flex-preferred-size" => |*v| .{ .@"flex-preferred-size" = .{ v[0].deepClone(allocator), v[1] } }, + .@"margin-top" => |*v| .{ .@"margin-top" = v.deepClone(allocator) }, + .@"margin-bottom" => |*v| .{ .@"margin-bottom" = v.deepClone(allocator) }, + .@"margin-left" => |*v| .{ .@"margin-left" = v.deepClone(allocator) }, + .@"margin-right" => |*v| .{ .@"margin-right" = v.deepClone(allocator) }, + .@"margin-block-start" => |*v| .{ .@"margin-block-start" = v.deepClone(allocator) }, + .@"margin-block-end" => |*v| .{ .@"margin-block-end" = v.deepClone(allocator) }, + .@"margin-inline-start" => |*v| .{ .@"margin-inline-start" = v.deepClone(allocator) }, + .@"margin-inline-end" => |*v| .{ .@"margin-inline-end" = v.deepClone(allocator) }, + .@"margin-block" => |*v| .{ .@"margin-block" = v.deepClone(allocator) }, + .@"margin-inline" => |*v| .{ .@"margin-inline" = v.deepClone(allocator) }, + .margin => |*v| .{ .margin = v.deepClone(allocator) }, + .@"padding-top" => |*v| .{ .@"padding-top" = v.deepClone(allocator) }, + .@"padding-bottom" => |*v| .{ .@"padding-bottom" = v.deepClone(allocator) }, + .@"padding-left" => |*v| .{ .@"padding-left" = v.deepClone(allocator) }, + .@"padding-right" => |*v| .{ .@"padding-right" = v.deepClone(allocator) }, + .@"padding-block-start" => |*v| .{ .@"padding-block-start" = v.deepClone(allocator) }, + .@"padding-block-end" => |*v| .{ .@"padding-block-end" = v.deepClone(allocator) }, + .@"padding-inline-start" => |*v| .{ .@"padding-inline-start" = v.deepClone(allocator) }, + .@"padding-inline-end" => |*v| .{ .@"padding-inline-end" = v.deepClone(allocator) }, + .@"padding-block" => |*v| .{ .@"padding-block" = v.deepClone(allocator) }, + .@"padding-inline" => |*v| .{ .@"padding-inline" = v.deepClone(allocator) }, + .padding => |*v| .{ .padding = v.deepClone(allocator) }, + .@"scroll-margin-top" => |*v| .{ .@"scroll-margin-top" = v.deepClone(allocator) }, + .@"scroll-margin-bottom" => |*v| .{ .@"scroll-margin-bottom" = v.deepClone(allocator) }, + .@"scroll-margin-left" => |*v| .{ .@"scroll-margin-left" = v.deepClone(allocator) }, + .@"scroll-margin-right" => |*v| .{ .@"scroll-margin-right" = v.deepClone(allocator) }, + .@"scroll-margin-block-start" => |*v| .{ .@"scroll-margin-block-start" = v.deepClone(allocator) }, + .@"scroll-margin-block-end" => |*v| .{ .@"scroll-margin-block-end" = v.deepClone(allocator) }, + .@"scroll-margin-inline-start" => |*v| .{ .@"scroll-margin-inline-start" = v.deepClone(allocator) }, + .@"scroll-margin-inline-end" => |*v| .{ .@"scroll-margin-inline-end" = v.deepClone(allocator) }, + .@"scroll-margin-block" => |*v| .{ .@"scroll-margin-block" = v.deepClone(allocator) }, + .@"scroll-margin-inline" => |*v| .{ .@"scroll-margin-inline" = v.deepClone(allocator) }, + .@"scroll-margin" => |*v| .{ .@"scroll-margin" = v.deepClone(allocator) }, + .@"scroll-padding-top" => |*v| .{ .@"scroll-padding-top" = v.deepClone(allocator) }, + .@"scroll-padding-bottom" => |*v| .{ .@"scroll-padding-bottom" = v.deepClone(allocator) }, + .@"scroll-padding-left" => |*v| .{ .@"scroll-padding-left" = v.deepClone(allocator) }, + .@"scroll-padding-right" => |*v| .{ .@"scroll-padding-right" = v.deepClone(allocator) }, + .@"scroll-padding-block-start" => |*v| .{ .@"scroll-padding-block-start" = v.deepClone(allocator) }, + .@"scroll-padding-block-end" => |*v| .{ .@"scroll-padding-block-end" = v.deepClone(allocator) }, + .@"scroll-padding-inline-start" => |*v| .{ .@"scroll-padding-inline-start" = v.deepClone(allocator) }, + .@"scroll-padding-inline-end" => |*v| .{ .@"scroll-padding-inline-end" = v.deepClone(allocator) }, + .@"scroll-padding-block" => |*v| .{ .@"scroll-padding-block" = v.deepClone(allocator) }, + .@"scroll-padding-inline" => |*v| .{ .@"scroll-padding-inline" = v.deepClone(allocator) }, + .@"scroll-padding" => |*v| .{ .@"scroll-padding" = v.deepClone(allocator) }, + .@"font-weight" => |*v| .{ .@"font-weight" = v.deepClone(allocator) }, + .@"font-size" => |*v| .{ .@"font-size" = v.deepClone(allocator) }, + .@"font-stretch" => |*v| .{ .@"font-stretch" = v.deepClone(allocator) }, + .@"font-family" => |*v| .{ .@"font-family" = css.generic.deepClone(BabyList(FontFamily), v, allocator) }, + .@"font-style" => |*v| .{ .@"font-style" = v.deepClone(allocator) }, + .@"font-variant-caps" => |*v| .{ .@"font-variant-caps" = v.deepClone(allocator) }, + .@"line-height" => |*v| .{ .@"line-height" = v.deepClone(allocator) }, + .font => |*v| .{ .font = v.deepClone(allocator) }, + .@"text-decoration-color" => |*v| .{ .@"text-decoration-color" = .{ v[0].deepClone(allocator), v[1] } }, + .@"text-emphasis-color" => |*v| .{ .@"text-emphasis-color" = .{ v[0].deepClone(allocator), v[1] } }, + .direction => |*v| .{ .direction = v.deepClone(allocator) }, + .composes => |*v| .{ .composes = v.deepClone(allocator) }, + .@"mask-image" => |*v| .{ .@"mask-image" = .{ v[0].deepClone(allocator), v[1] } }, + .@"mask-mode" => |*v| .{ .@"mask-mode" = v.deepClone(allocator) }, + .@"mask-repeat" => |*v| .{ .@"mask-repeat" = .{ v[0].deepClone(allocator), v[1] } }, + .@"mask-position-x" => |*v| .{ .@"mask-position-x" = v.deepClone(allocator) }, + .@"mask-position-y" => |*v| .{ .@"mask-position-y" = v.deepClone(allocator) }, + .@"mask-position" => |*v| .{ .@"mask-position" = .{ v[0].deepClone(allocator), v[1] } }, + .@"mask-clip" => |*v| .{ .@"mask-clip" = .{ v[0].deepClone(allocator), v[1] } }, + .@"mask-origin" => |*v| .{ .@"mask-origin" = .{ v[0].deepClone(allocator), v[1] } }, + .@"mask-size" => |*v| .{ .@"mask-size" = .{ v[0].deepClone(allocator), v[1] } }, + .@"mask-composite" => |*v| .{ .@"mask-composite" = v.deepClone(allocator) }, + .@"mask-type" => |*v| .{ .@"mask-type" = v.deepClone(allocator) }, + .mask => |*v| .{ .mask = .{ v[0].deepClone(allocator), v[1] } }, + .@"mask-border-source" => |*v| .{ .@"mask-border-source" = v.deepClone(allocator) }, + .@"mask-border-mode" => |*v| .{ .@"mask-border-mode" = v.deepClone(allocator) }, + .@"mask-border-slice" => |*v| .{ .@"mask-border-slice" = v.deepClone(allocator) }, + .@"mask-border-width" => |*v| .{ .@"mask-border-width" = v.deepClone(allocator) }, + .@"mask-border-outset" => |*v| .{ .@"mask-border-outset" = v.deepClone(allocator) }, + .@"mask-border-repeat" => |*v| .{ .@"mask-border-repeat" = v.deepClone(allocator) }, + .@"mask-border" => |*v| .{ .@"mask-border" = v.deepClone(allocator) }, + .@"-webkit-mask-composite" => |*v| .{ .@"-webkit-mask-composite" = v.deepClone(allocator) }, + .@"mask-source-type" => |*v| .{ .@"mask-source-type" = .{ v[0].deepClone(allocator), v[1] } }, + .@"mask-box-image" => |*v| .{ .@"mask-box-image" = .{ v[0].deepClone(allocator), v[1] } }, + .@"mask-box-image-source" => |*v| .{ .@"mask-box-image-source" = .{ v[0].deepClone(allocator), v[1] } }, + .@"mask-box-image-slice" => |*v| .{ .@"mask-box-image-slice" = .{ v[0].deepClone(allocator), v[1] } }, + .@"mask-box-image-width" => |*v| .{ .@"mask-box-image-width" = .{ v[0].deepClone(allocator), v[1] } }, + .@"mask-box-image-outset" => |*v| .{ .@"mask-box-image-outset" = .{ v[0].deepClone(allocator), v[1] } }, + .@"mask-box-image-repeat" => |*v| .{ .@"mask-box-image-repeat" = .{ v[0].deepClone(allocator), v[1] } }, + .all => |*a| return .{ .all = a.deepClone(allocator) }, + .unparsed => |*u| return .{ .unparsed = u.deepClone(allocator) }, + .custom => |*c| return .{ .custom = c.deepClone(allocator) }, + }; + } + + /// We're going to have this empty for now since not every property has a deinit function. + /// It's not strictly necessary since all allocations are into an arena. + /// It's mostly intended as a performance optimization in the case where mimalloc arena is used, + /// since it can reclaim the memory and use it for subsequent allocations. + /// I haven't benchmarked that though, so I don't actually know how much faster it would actually make it. + pub fn deinit(this: *@This(), allocator: std.mem.Allocator) void { + _ = this; + _ = allocator; + } + pub inline fn __toCssHelper(this: *const Property) struct { []const u8, VendorPrefix } { return switch (this.*) { .@"background-color" => .{ "background-color", VendorPrefix{ .none = true } }, + .@"background-image" => .{ "background-image", VendorPrefix{ .none = true } }, + .@"background-position-x" => .{ "background-position-x", VendorPrefix{ .none = true } }, + .@"background-position-y" => .{ "background-position-y", VendorPrefix{ .none = true } }, + .@"background-position" => .{ "background-position", VendorPrefix{ .none = true } }, + .@"background-size" => .{ "background-size", VendorPrefix{ .none = true } }, + .@"background-repeat" => .{ "background-repeat", VendorPrefix{ .none = true } }, + .@"background-attachment" => .{ "background-attachment", VendorPrefix{ .none = true } }, + .@"background-clip" => |*x| .{ "background-clip", x.@"1" }, + .@"background-origin" => .{ "background-origin", VendorPrefix{ .none = true } }, + .background => .{ "background", VendorPrefix{ .none = true } }, + .@"box-shadow" => |*x| .{ "box-shadow", x.@"1" }, + .opacity => .{ "opacity", VendorPrefix{ .none = true } }, .color => .{ "color", VendorPrefix{ .none = true } }, + .display => .{ "display", VendorPrefix{ .none = true } }, + .visibility => .{ "visibility", VendorPrefix{ .none = true } }, + .width => .{ "width", VendorPrefix{ .none = true } }, + .height => .{ "height", VendorPrefix{ .none = true } }, + .@"min-width" => .{ "min-width", VendorPrefix{ .none = true } }, + .@"min-height" => .{ "min-height", VendorPrefix{ .none = true } }, + .@"max-width" => .{ "max-width", VendorPrefix{ .none = true } }, + .@"max-height" => .{ "max-height", VendorPrefix{ .none = true } }, + .@"block-size" => .{ "block-size", VendorPrefix{ .none = true } }, + .@"inline-size" => .{ "inline-size", VendorPrefix{ .none = true } }, + .@"min-block-size" => .{ "min-block-size", VendorPrefix{ .none = true } }, + .@"min-inline-size" => .{ "min-inline-size", VendorPrefix{ .none = true } }, + .@"max-block-size" => .{ "max-block-size", VendorPrefix{ .none = true } }, + .@"max-inline-size" => .{ "max-inline-size", VendorPrefix{ .none = true } }, + .@"box-sizing" => |*x| .{ "box-sizing", x.@"1" }, + .@"aspect-ratio" => .{ "aspect-ratio", VendorPrefix{ .none = true } }, + .overflow => .{ "overflow", VendorPrefix{ .none = true } }, + .@"overflow-x" => .{ "overflow-x", VendorPrefix{ .none = true } }, + .@"overflow-y" => .{ "overflow-y", VendorPrefix{ .none = true } }, + .@"text-overflow" => |*x| .{ "text-overflow", x.@"1" }, + .position => .{ "position", VendorPrefix{ .none = true } }, + .top => .{ "top", VendorPrefix{ .none = true } }, + .bottom => .{ "bottom", VendorPrefix{ .none = true } }, + .left => .{ "left", VendorPrefix{ .none = true } }, + .right => .{ "right", VendorPrefix{ .none = true } }, + .@"inset-block-start" => .{ "inset-block-start", VendorPrefix{ .none = true } }, + .@"inset-block-end" => .{ "inset-block-end", VendorPrefix{ .none = true } }, + .@"inset-inline-start" => .{ "inset-inline-start", VendorPrefix{ .none = true } }, + .@"inset-inline-end" => .{ "inset-inline-end", VendorPrefix{ .none = true } }, + .@"inset-block" => .{ "inset-block", VendorPrefix{ .none = true } }, + .@"inset-inline" => .{ "inset-inline", VendorPrefix{ .none = true } }, + .inset => .{ "inset", VendorPrefix{ .none = true } }, .@"border-spacing" => .{ "border-spacing", VendorPrefix{ .none = true } }, .@"border-top-color" => .{ "border-top-color", VendorPrefix{ .none = true } }, .@"border-bottom-color" => .{ "border-bottom-color", VendorPrefix{ .none = true } }, @@ -493,14 +6199,174 @@ pub const Property = union(PropertyIdTag) { .@"border-right-style" => .{ "border-right-style", VendorPrefix{ .none = true } }, .@"border-block-start-style" => .{ "border-block-start-style", VendorPrefix{ .none = true } }, .@"border-block-end-style" => .{ "border-block-end-style", VendorPrefix{ .none = true } }, + .@"border-inline-start-style" => .{ "border-inline-start-style", VendorPrefix{ .none = true } }, + .@"border-inline-end-style" => .{ "border-inline-end-style", VendorPrefix{ .none = true } }, .@"border-top-width" => .{ "border-top-width", VendorPrefix{ .none = true } }, .@"border-bottom-width" => .{ "border-bottom-width", VendorPrefix{ .none = true } }, .@"border-left-width" => .{ "border-left-width", VendorPrefix{ .none = true } }, .@"border-right-width" => .{ "border-right-width", VendorPrefix{ .none = true } }, + .@"border-block-start-width" => .{ "border-block-start-width", VendorPrefix{ .none = true } }, + .@"border-block-end-width" => .{ "border-block-end-width", VendorPrefix{ .none = true } }, + .@"border-inline-start-width" => .{ "border-inline-start-width", VendorPrefix{ .none = true } }, + .@"border-inline-end-width" => .{ "border-inline-end-width", VendorPrefix{ .none = true } }, + .@"border-top-left-radius" => |*x| .{ "border-top-left-radius", x.@"1" }, + .@"border-top-right-radius" => |*x| .{ "border-top-right-radius", x.@"1" }, + .@"border-bottom-left-radius" => |*x| .{ "border-bottom-left-radius", x.@"1" }, + .@"border-bottom-right-radius" => |*x| .{ "border-bottom-right-radius", x.@"1" }, + .@"border-start-start-radius" => .{ "border-start-start-radius", VendorPrefix{ .none = true } }, + .@"border-start-end-radius" => .{ "border-start-end-radius", VendorPrefix{ .none = true } }, + .@"border-end-start-radius" => .{ "border-end-start-radius", VendorPrefix{ .none = true } }, + .@"border-end-end-radius" => .{ "border-end-end-radius", VendorPrefix{ .none = true } }, + .@"border-radius" => |*x| .{ "border-radius", x.@"1" }, + .@"border-image-source" => .{ "border-image-source", VendorPrefix{ .none = true } }, + .@"border-image-outset" => .{ "border-image-outset", VendorPrefix{ .none = true } }, + .@"border-image-repeat" => .{ "border-image-repeat", VendorPrefix{ .none = true } }, + .@"border-image-width" => .{ "border-image-width", VendorPrefix{ .none = true } }, + .@"border-image-slice" => .{ "border-image-slice", VendorPrefix{ .none = true } }, + .@"border-image" => |*x| .{ "border-image", x.@"1" }, + .@"border-color" => .{ "border-color", VendorPrefix{ .none = true } }, + .@"border-style" => .{ "border-style", VendorPrefix{ .none = true } }, + .@"border-width" => .{ "border-width", VendorPrefix{ .none = true } }, + .@"border-block-color" => .{ "border-block-color", VendorPrefix{ .none = true } }, + .@"border-block-style" => .{ "border-block-style", VendorPrefix{ .none = true } }, + .@"border-block-width" => .{ "border-block-width", VendorPrefix{ .none = true } }, + .@"border-inline-color" => .{ "border-inline-color", VendorPrefix{ .none = true } }, + .@"border-inline-style" => .{ "border-inline-style", VendorPrefix{ .none = true } }, + .@"border-inline-width" => .{ "border-inline-width", VendorPrefix{ .none = true } }, + .border => .{ "border", VendorPrefix{ .none = true } }, + .@"border-top" => .{ "border-top", VendorPrefix{ .none = true } }, + .@"border-bottom" => .{ "border-bottom", VendorPrefix{ .none = true } }, + .@"border-left" => .{ "border-left", VendorPrefix{ .none = true } }, + .@"border-right" => .{ "border-right", VendorPrefix{ .none = true } }, + .@"border-block" => .{ "border-block", VendorPrefix{ .none = true } }, + .@"border-block-start" => .{ "border-block-start", VendorPrefix{ .none = true } }, + .@"border-block-end" => .{ "border-block-end", VendorPrefix{ .none = true } }, + .@"border-inline" => .{ "border-inline", VendorPrefix{ .none = true } }, + .@"border-inline-start" => .{ "border-inline-start", VendorPrefix{ .none = true } }, + .@"border-inline-end" => .{ "border-inline-end", VendorPrefix{ .none = true } }, + .outline => .{ "outline", VendorPrefix{ .none = true } }, .@"outline-color" => .{ "outline-color", VendorPrefix{ .none = true } }, + .@"outline-style" => .{ "outline-style", VendorPrefix{ .none = true } }, + .@"outline-width" => .{ "outline-width", VendorPrefix{ .none = true } }, + .@"flex-direction" => |*x| .{ "flex-direction", x.@"1" }, + .@"flex-wrap" => |*x| .{ "flex-wrap", x.@"1" }, + .@"flex-flow" => |*x| .{ "flex-flow", x.@"1" }, + .@"flex-grow" => |*x| .{ "flex-grow", x.@"1" }, + .@"flex-shrink" => |*x| .{ "flex-shrink", x.@"1" }, + .@"flex-basis" => |*x| .{ "flex-basis", x.@"1" }, + .flex => |*x| .{ "flex", x.@"1" }, + .order => |*x| .{ "order", x.@"1" }, + .@"align-content" => |*x| .{ "align-content", x.@"1" }, + .@"justify-content" => |*x| .{ "justify-content", x.@"1" }, + .@"place-content" => .{ "place-content", VendorPrefix{ .none = true } }, + .@"align-self" => |*x| .{ "align-self", x.@"1" }, + .@"justify-self" => .{ "justify-self", VendorPrefix{ .none = true } }, + .@"place-self" => .{ "place-self", VendorPrefix{ .none = true } }, + .@"align-items" => |*x| .{ "align-items", x.@"1" }, + .@"justify-items" => .{ "justify-items", VendorPrefix{ .none = true } }, + .@"place-items" => .{ "place-items", VendorPrefix{ .none = true } }, + .@"row-gap" => .{ "row-gap", VendorPrefix{ .none = true } }, + .@"column-gap" => .{ "column-gap", VendorPrefix{ .none = true } }, + .gap => .{ "gap", VendorPrefix{ .none = true } }, + .@"box-orient" => |*x| .{ "box-orient", x.@"1" }, + .@"box-direction" => |*x| .{ "box-direction", x.@"1" }, + .@"box-ordinal-group" => |*x| .{ "box-ordinal-group", x.@"1" }, + .@"box-align" => |*x| .{ "box-align", x.@"1" }, + .@"box-flex" => |*x| .{ "box-flex", x.@"1" }, + .@"box-flex-group" => |*x| .{ "box-flex-group", x.@"1" }, + .@"box-pack" => |*x| .{ "box-pack", x.@"1" }, + .@"box-lines" => |*x| .{ "box-lines", x.@"1" }, + .@"flex-pack" => |*x| .{ "flex-pack", x.@"1" }, + .@"flex-order" => |*x| .{ "flex-order", x.@"1" }, + .@"flex-align" => |*x| .{ "flex-align", x.@"1" }, + .@"flex-item-align" => |*x| .{ "flex-item-align", x.@"1" }, + .@"flex-line-pack" => |*x| .{ "flex-line-pack", x.@"1" }, + .@"flex-positive" => |*x| .{ "flex-positive", x.@"1" }, + .@"flex-negative" => |*x| .{ "flex-negative", x.@"1" }, + .@"flex-preferred-size" => |*x| .{ "flex-preferred-size", x.@"1" }, + .@"margin-top" => .{ "margin-top", VendorPrefix{ .none = true } }, + .@"margin-bottom" => .{ "margin-bottom", VendorPrefix{ .none = true } }, + .@"margin-left" => .{ "margin-left", VendorPrefix{ .none = true } }, + .@"margin-right" => .{ "margin-right", VendorPrefix{ .none = true } }, + .@"margin-block-start" => .{ "margin-block-start", VendorPrefix{ .none = true } }, + .@"margin-block-end" => .{ "margin-block-end", VendorPrefix{ .none = true } }, + .@"margin-inline-start" => .{ "margin-inline-start", VendorPrefix{ .none = true } }, + .@"margin-inline-end" => .{ "margin-inline-end", VendorPrefix{ .none = true } }, + .@"margin-block" => .{ "margin-block", VendorPrefix{ .none = true } }, + .@"margin-inline" => .{ "margin-inline", VendorPrefix{ .none = true } }, + .margin => .{ "margin", VendorPrefix{ .none = true } }, + .@"padding-top" => .{ "padding-top", VendorPrefix{ .none = true } }, + .@"padding-bottom" => .{ "padding-bottom", VendorPrefix{ .none = true } }, + .@"padding-left" => .{ "padding-left", VendorPrefix{ .none = true } }, + .@"padding-right" => .{ "padding-right", VendorPrefix{ .none = true } }, + .@"padding-block-start" => .{ "padding-block-start", VendorPrefix{ .none = true } }, + .@"padding-block-end" => .{ "padding-block-end", VendorPrefix{ .none = true } }, + .@"padding-inline-start" => .{ "padding-inline-start", VendorPrefix{ .none = true } }, + .@"padding-inline-end" => .{ "padding-inline-end", VendorPrefix{ .none = true } }, + .@"padding-block" => .{ "padding-block", VendorPrefix{ .none = true } }, + .@"padding-inline" => .{ "padding-inline", VendorPrefix{ .none = true } }, + .padding => .{ "padding", VendorPrefix{ .none = true } }, + .@"scroll-margin-top" => .{ "scroll-margin-top", VendorPrefix{ .none = true } }, + .@"scroll-margin-bottom" => .{ "scroll-margin-bottom", VendorPrefix{ .none = true } }, + .@"scroll-margin-left" => .{ "scroll-margin-left", VendorPrefix{ .none = true } }, + .@"scroll-margin-right" => .{ "scroll-margin-right", VendorPrefix{ .none = true } }, + .@"scroll-margin-block-start" => .{ "scroll-margin-block-start", VendorPrefix{ .none = true } }, + .@"scroll-margin-block-end" => .{ "scroll-margin-block-end", VendorPrefix{ .none = true } }, + .@"scroll-margin-inline-start" => .{ "scroll-margin-inline-start", VendorPrefix{ .none = true } }, + .@"scroll-margin-inline-end" => .{ "scroll-margin-inline-end", VendorPrefix{ .none = true } }, + .@"scroll-margin-block" => .{ "scroll-margin-block", VendorPrefix{ .none = true } }, + .@"scroll-margin-inline" => .{ "scroll-margin-inline", VendorPrefix{ .none = true } }, + .@"scroll-margin" => .{ "scroll-margin", VendorPrefix{ .none = true } }, + .@"scroll-padding-top" => .{ "scroll-padding-top", VendorPrefix{ .none = true } }, + .@"scroll-padding-bottom" => .{ "scroll-padding-bottom", VendorPrefix{ .none = true } }, + .@"scroll-padding-left" => .{ "scroll-padding-left", VendorPrefix{ .none = true } }, + .@"scroll-padding-right" => .{ "scroll-padding-right", VendorPrefix{ .none = true } }, + .@"scroll-padding-block-start" => .{ "scroll-padding-block-start", VendorPrefix{ .none = true } }, + .@"scroll-padding-block-end" => .{ "scroll-padding-block-end", VendorPrefix{ .none = true } }, + .@"scroll-padding-inline-start" => .{ "scroll-padding-inline-start", VendorPrefix{ .none = true } }, + .@"scroll-padding-inline-end" => .{ "scroll-padding-inline-end", VendorPrefix{ .none = true } }, + .@"scroll-padding-block" => .{ "scroll-padding-block", VendorPrefix{ .none = true } }, + .@"scroll-padding-inline" => .{ "scroll-padding-inline", VendorPrefix{ .none = true } }, + .@"scroll-padding" => .{ "scroll-padding", VendorPrefix{ .none = true } }, + .@"font-weight" => .{ "font-weight", VendorPrefix{ .none = true } }, + .@"font-size" => .{ "font-size", VendorPrefix{ .none = true } }, + .@"font-stretch" => .{ "font-stretch", VendorPrefix{ .none = true } }, + .@"font-family" => .{ "font-family", VendorPrefix{ .none = true } }, + .@"font-style" => .{ "font-style", VendorPrefix{ .none = true } }, + .@"font-variant-caps" => .{ "font-variant-caps", VendorPrefix{ .none = true } }, + .@"line-height" => .{ "line-height", VendorPrefix{ .none = true } }, + .font => .{ "font", VendorPrefix{ .none = true } }, .@"text-decoration-color" => |*x| .{ "text-decoration-color", x.@"1" }, .@"text-emphasis-color" => |*x| .{ "text-emphasis-color", x.@"1" }, + .direction => .{ "direction", VendorPrefix{ .none = true } }, .composes => .{ "composes", VendorPrefix{ .none = true } }, + .@"mask-image" => |*x| .{ "mask-image", x.@"1" }, + .@"mask-mode" => .{ "mask-mode", VendorPrefix{ .none = true } }, + .@"mask-repeat" => |*x| .{ "mask-repeat", x.@"1" }, + .@"mask-position-x" => .{ "mask-position-x", VendorPrefix{ .none = true } }, + .@"mask-position-y" => .{ "mask-position-y", VendorPrefix{ .none = true } }, + .@"mask-position" => |*x| .{ "mask-position", x.@"1" }, + .@"mask-clip" => |*x| .{ "mask-clip", x.@"1" }, + .@"mask-origin" => |*x| .{ "mask-origin", x.@"1" }, + .@"mask-size" => |*x| .{ "mask-size", x.@"1" }, + .@"mask-composite" => .{ "mask-composite", VendorPrefix{ .none = true } }, + .@"mask-type" => .{ "mask-type", VendorPrefix{ .none = true } }, + .mask => |*x| .{ "mask", x.@"1" }, + .@"mask-border-source" => .{ "mask-border-source", VendorPrefix{ .none = true } }, + .@"mask-border-mode" => .{ "mask-border-mode", VendorPrefix{ .none = true } }, + .@"mask-border-slice" => .{ "mask-border-slice", VendorPrefix{ .none = true } }, + .@"mask-border-width" => .{ "mask-border-width", VendorPrefix{ .none = true } }, + .@"mask-border-outset" => .{ "mask-border-outset", VendorPrefix{ .none = true } }, + .@"mask-border-repeat" => .{ "mask-border-repeat", VendorPrefix{ .none = true } }, + .@"mask-border" => .{ "mask-border", VendorPrefix{ .none = true } }, + .@"-webkit-mask-composite" => .{ "-webkit-mask-composite", VendorPrefix{ .none = true } }, + .@"mask-source-type" => |*x| .{ "mask-source-type", x.@"1" }, + .@"mask-box-image" => |*x| .{ "mask-box-image", x.@"1" }, + .@"mask-box-image-source" => |*x| .{ "mask-box-image-source", x.@"1" }, + .@"mask-box-image-slice" => |*x| .{ "mask-box-image-slice", x.@"1" }, + .@"mask-box-image-width" => |*x| .{ "mask-box-image-width", x.@"1" }, + .@"mask-box-image-outset" => |*x| .{ "mask-box-image-outset", x.@"1" }, + .@"mask-box-image-repeat" => |*x| .{ "mask-box-image-repeat", x.@"1" }, .all => .{ "all", VendorPrefix{ .none = true } }, .unparsed => |*unparsed| brk: { var prefix = unparsed.property_id.prefix(); @@ -517,7 +6383,51 @@ pub const Property = union(PropertyIdTag) { pub fn valueToCss(this: *const Property, comptime W: type, dest: *css.Printer(W)) PrintErr!void { return switch (this.*) { .@"background-color" => |*value| value.toCss(W, dest), + .@"background-image" => |*value| value.toCss(W, dest), + .@"background-position-x" => |*value| value.toCss(W, dest), + .@"background-position-y" => |*value| value.toCss(W, dest), + .@"background-position" => |*value| value.toCss(W, dest), + .@"background-size" => |*value| value.toCss(W, dest), + .@"background-repeat" => |*value| value.toCss(W, dest), + .@"background-attachment" => |*value| value.toCss(W, dest), + .@"background-clip" => |*value| value[0].toCss(W, dest), + .@"background-origin" => |*value| value.toCss(W, dest), + .background => |*value| value.toCss(W, dest), + .@"box-shadow" => |*value| value[0].toCss(W, dest), + .opacity => |*value| value.toCss(W, dest), .color => |*value| value.toCss(W, dest), + .display => |*value| value.toCss(W, dest), + .visibility => |*value| value.toCss(W, dest), + .width => |*value| value.toCss(W, dest), + .height => |*value| value.toCss(W, dest), + .@"min-width" => |*value| value.toCss(W, dest), + .@"min-height" => |*value| value.toCss(W, dest), + .@"max-width" => |*value| value.toCss(W, dest), + .@"max-height" => |*value| value.toCss(W, dest), + .@"block-size" => |*value| value.toCss(W, dest), + .@"inline-size" => |*value| value.toCss(W, dest), + .@"min-block-size" => |*value| value.toCss(W, dest), + .@"min-inline-size" => |*value| value.toCss(W, dest), + .@"max-block-size" => |*value| value.toCss(W, dest), + .@"max-inline-size" => |*value| value.toCss(W, dest), + .@"box-sizing" => |*value| value[0].toCss(W, dest), + .@"aspect-ratio" => |*value| value.toCss(W, dest), + .overflow => |*value| value.toCss(W, dest), + .@"overflow-x" => |*value| value.toCss(W, dest), + .@"overflow-y" => |*value| value.toCss(W, dest), + .@"text-overflow" => |*value| value[0].toCss(W, dest), + .position => |*value| value.toCss(W, dest), + .top => |*value| value.toCss(W, dest), + .bottom => |*value| value.toCss(W, dest), + .left => |*value| value.toCss(W, dest), + .right => |*value| value.toCss(W, dest), + .@"inset-block-start" => |*value| value.toCss(W, dest), + .@"inset-block-end" => |*value| value.toCss(W, dest), + .@"inset-inline-start" => |*value| value.toCss(W, dest), + .@"inset-inline-end" => |*value| value.toCss(W, dest), + .@"inset-block" => |*value| value.toCss(W, dest), + .@"inset-inline" => |*value| value.toCss(W, dest), + .inset => |*value| value.toCss(W, dest), .@"border-spacing" => |*value| value.toCss(W, dest), .@"border-top-color" => |*value| value.toCss(W, dest), .@"border-bottom-color" => |*value| value.toCss(W, dest), @@ -533,14 +6443,174 @@ pub const Property = union(PropertyIdTag) { .@"border-right-style" => |*value| value.toCss(W, dest), .@"border-block-start-style" => |*value| value.toCss(W, dest), .@"border-block-end-style" => |*value| value.toCss(W, dest), + .@"border-inline-start-style" => |*value| value.toCss(W, dest), + .@"border-inline-end-style" => |*value| value.toCss(W, dest), .@"border-top-width" => |*value| value.toCss(W, dest), .@"border-bottom-width" => |*value| value.toCss(W, dest), .@"border-left-width" => |*value| value.toCss(W, dest), .@"border-right-width" => |*value| value.toCss(W, dest), + .@"border-block-start-width" => |*value| value.toCss(W, dest), + .@"border-block-end-width" => |*value| value.toCss(W, dest), + .@"border-inline-start-width" => |*value| value.toCss(W, dest), + .@"border-inline-end-width" => |*value| value.toCss(W, dest), + .@"border-top-left-radius" => |*value| value[0].toCss(W, dest), + .@"border-top-right-radius" => |*value| value[0].toCss(W, dest), + .@"border-bottom-left-radius" => |*value| value[0].toCss(W, dest), + .@"border-bottom-right-radius" => |*value| value[0].toCss(W, dest), + .@"border-start-start-radius" => |*value| value.toCss(W, dest), + .@"border-start-end-radius" => |*value| value.toCss(W, dest), + .@"border-end-start-radius" => |*value| value.toCss(W, dest), + .@"border-end-end-radius" => |*value| value.toCss(W, dest), + .@"border-radius" => |*value| value[0].toCss(W, dest), + .@"border-image-source" => |*value| value.toCss(W, dest), + .@"border-image-outset" => |*value| value.toCss(W, dest), + .@"border-image-repeat" => |*value| value.toCss(W, dest), + .@"border-image-width" => |*value| value.toCss(W, dest), + .@"border-image-slice" => |*value| value.toCss(W, dest), + .@"border-image" => |*value| value[0].toCss(W, dest), + .@"border-color" => |*value| value.toCss(W, dest), + .@"border-style" => |*value| value.toCss(W, dest), + .@"border-width" => |*value| value.toCss(W, dest), + .@"border-block-color" => |*value| value.toCss(W, dest), + .@"border-block-style" => |*value| value.toCss(W, dest), + .@"border-block-width" => |*value| value.toCss(W, dest), + .@"border-inline-color" => |*value| value.toCss(W, dest), + .@"border-inline-style" => |*value| value.toCss(W, dest), + .@"border-inline-width" => |*value| value.toCss(W, dest), + .border => |*value| value.toCss(W, dest), + .@"border-top" => |*value| value.toCss(W, dest), + .@"border-bottom" => |*value| value.toCss(W, dest), + .@"border-left" => |*value| value.toCss(W, dest), + .@"border-right" => |*value| value.toCss(W, dest), + .@"border-block" => |*value| value.toCss(W, dest), + .@"border-block-start" => |*value| value.toCss(W, dest), + .@"border-block-end" => |*value| value.toCss(W, dest), + .@"border-inline" => |*value| value.toCss(W, dest), + .@"border-inline-start" => |*value| value.toCss(W, dest), + .@"border-inline-end" => |*value| value.toCss(W, dest), + .outline => |*value| value.toCss(W, dest), .@"outline-color" => |*value| value.toCss(W, dest), + .@"outline-style" => |*value| value.toCss(W, dest), + .@"outline-width" => |*value| value.toCss(W, dest), + .@"flex-direction" => |*value| value[0].toCss(W, dest), + .@"flex-wrap" => |*value| value[0].toCss(W, dest), + .@"flex-flow" => |*value| value[0].toCss(W, dest), + .@"flex-grow" => |*value| CSSNumberFns.toCss(&value[0], W, dest), + .@"flex-shrink" => |*value| CSSNumberFns.toCss(&value[0], W, dest), + .@"flex-basis" => |*value| value[0].toCss(W, dest), + .flex => |*value| value[0].toCss(W, dest), + .order => |*value| CSSIntegerFns.toCss(&value[0], W, dest), + .@"align-content" => |*value| value[0].toCss(W, dest), + .@"justify-content" => |*value| value[0].toCss(W, dest), + .@"place-content" => |*value| value.toCss(W, dest), + .@"align-self" => |*value| value[0].toCss(W, dest), + .@"justify-self" => |*value| value.toCss(W, dest), + .@"place-self" => |*value| value.toCss(W, dest), + .@"align-items" => |*value| value[0].toCss(W, dest), + .@"justify-items" => |*value| value.toCss(W, dest), + .@"place-items" => |*value| value.toCss(W, dest), + .@"row-gap" => |*value| value.toCss(W, dest), + .@"column-gap" => |*value| value.toCss(W, dest), + .gap => |*value| value.toCss(W, dest), + .@"box-orient" => |*value| value[0].toCss(W, dest), + .@"box-direction" => |*value| value[0].toCss(W, dest), + .@"box-ordinal-group" => |*value| CSSIntegerFns.toCss(&value[0], W, dest), + .@"box-align" => |*value| value[0].toCss(W, dest), + .@"box-flex" => |*value| CSSNumberFns.toCss(&value[0], W, dest), + .@"box-flex-group" => |*value| CSSIntegerFns.toCss(&value[0], W, dest), + .@"box-pack" => |*value| value[0].toCss(W, dest), + .@"box-lines" => |*value| value[0].toCss(W, dest), + .@"flex-pack" => |*value| value[0].toCss(W, dest), + .@"flex-order" => |*value| CSSIntegerFns.toCss(&value[0], W, dest), + .@"flex-align" => |*value| value[0].toCss(W, dest), + .@"flex-item-align" => |*value| value[0].toCss(W, dest), + .@"flex-line-pack" => |*value| value[0].toCss(W, dest), + .@"flex-positive" => |*value| CSSNumberFns.toCss(&value[0], W, dest), + .@"flex-negative" => |*value| CSSNumberFns.toCss(&value[0], W, dest), + .@"flex-preferred-size" => |*value| value[0].toCss(W, dest), + .@"margin-top" => |*value| value.toCss(W, dest), + .@"margin-bottom" => |*value| value.toCss(W, dest), + .@"margin-left" => |*value| value.toCss(W, dest), + .@"margin-right" => |*value| value.toCss(W, dest), + .@"margin-block-start" => |*value| value.toCss(W, dest), + .@"margin-block-end" => |*value| value.toCss(W, dest), + .@"margin-inline-start" => |*value| value.toCss(W, dest), + .@"margin-inline-end" => |*value| value.toCss(W, dest), + .@"margin-block" => |*value| value.toCss(W, dest), + .@"margin-inline" => |*value| value.toCss(W, dest), + .margin => |*value| value.toCss(W, dest), + .@"padding-top" => |*value| value.toCss(W, dest), + .@"padding-bottom" => |*value| value.toCss(W, dest), + .@"padding-left" => |*value| value.toCss(W, dest), + .@"padding-right" => |*value| value.toCss(W, dest), + .@"padding-block-start" => |*value| value.toCss(W, dest), + .@"padding-block-end" => |*value| value.toCss(W, dest), + .@"padding-inline-start" => |*value| value.toCss(W, dest), + .@"padding-inline-end" => |*value| value.toCss(W, dest), + .@"padding-block" => |*value| value.toCss(W, dest), + .@"padding-inline" => |*value| value.toCss(W, dest), + .padding => |*value| value.toCss(W, dest), + .@"scroll-margin-top" => |*value| value.toCss(W, dest), + .@"scroll-margin-bottom" => |*value| value.toCss(W, dest), + .@"scroll-margin-left" => |*value| value.toCss(W, dest), + .@"scroll-margin-right" => |*value| value.toCss(W, dest), + .@"scroll-margin-block-start" => |*value| value.toCss(W, dest), + .@"scroll-margin-block-end" => |*value| value.toCss(W, dest), + .@"scroll-margin-inline-start" => |*value| value.toCss(W, dest), + .@"scroll-margin-inline-end" => |*value| value.toCss(W, dest), + .@"scroll-margin-block" => |*value| value.toCss(W, dest), + .@"scroll-margin-inline" => |*value| value.toCss(W, dest), + .@"scroll-margin" => |*value| value.toCss(W, dest), + .@"scroll-padding-top" => |*value| value.toCss(W, dest), + .@"scroll-padding-bottom" => |*value| value.toCss(W, dest), + .@"scroll-padding-left" => |*value| value.toCss(W, dest), + .@"scroll-padding-right" => |*value| value.toCss(W, dest), + .@"scroll-padding-block-start" => |*value| value.toCss(W, dest), + .@"scroll-padding-block-end" => |*value| value.toCss(W, dest), + .@"scroll-padding-inline-start" => |*value| value.toCss(W, dest), + .@"scroll-padding-inline-end" => |*value| value.toCss(W, dest), + .@"scroll-padding-block" => |*value| value.toCss(W, dest), + .@"scroll-padding-inline" => |*value| value.toCss(W, dest), + .@"scroll-padding" => |*value| value.toCss(W, dest), + .@"font-weight" => |*value| value.toCss(W, dest), + .@"font-size" => |*value| value.toCss(W, dest), + .@"font-stretch" => |*value| value.toCss(W, dest), + .@"font-family" => |*value| value.toCss(W, dest), + .@"font-style" => |*value| value.toCss(W, dest), + .@"font-variant-caps" => |*value| value.toCss(W, dest), + .@"line-height" => |*value| value.toCss(W, dest), + .font => |*value| value.toCss(W, dest), .@"text-decoration-color" => |*value| value[0].toCss(W, dest), .@"text-emphasis-color" => |*value| value[0].toCss(W, dest), + .direction => |*value| value.toCss(W, dest), .composes => |*value| value.toCss(W, dest), + .@"mask-image" => |*value| value[0].toCss(W, dest), + .@"mask-mode" => |*value| value.toCss(W, dest), + .@"mask-repeat" => |*value| value[0].toCss(W, dest), + .@"mask-position-x" => |*value| value.toCss(W, dest), + .@"mask-position-y" => |*value| value.toCss(W, dest), + .@"mask-position" => |*value| value[0].toCss(W, dest), + .@"mask-clip" => |*value| value[0].toCss(W, dest), + .@"mask-origin" => |*value| value[0].toCss(W, dest), + .@"mask-size" => |*value| value[0].toCss(W, dest), + .@"mask-composite" => |*value| value.toCss(W, dest), + .@"mask-type" => |*value| value.toCss(W, dest), + .mask => |*value| value[0].toCss(W, dest), + .@"mask-border-source" => |*value| value.toCss(W, dest), + .@"mask-border-mode" => |*value| value.toCss(W, dest), + .@"mask-border-slice" => |*value| value.toCss(W, dest), + .@"mask-border-width" => |*value| value.toCss(W, dest), + .@"mask-border-outset" => |*value| value.toCss(W, dest), + .@"mask-border-repeat" => |*value| value.toCss(W, dest), + .@"mask-border" => |*value| value.toCss(W, dest), + .@"-webkit-mask-composite" => |*value| value.toCss(W, dest), + .@"mask-source-type" => |*value| value[0].toCss(W, dest), + .@"mask-box-image" => |*value| value[0].toCss(W, dest), + .@"mask-box-image-source" => |*value| value[0].toCss(W, dest), + .@"mask-box-image-slice" => |*value| value[0].toCss(W, dest), + .@"mask-box-image-width" => |*value| value[0].toCss(W, dest), + .@"mask-box-image-outset" => |*value| value[0].toCss(W, dest), + .@"mask-box-image-repeat" => |*value| value[0].toCss(W, dest), .all => |*keyword| keyword.toCss(W, dest), .unparsed => |*unparsed| unparsed.value.toCss(W, dest, false), .custom => |*c| c.value.toCss(W, dest, c.name == .custom), @@ -549,16 +6619,360 @@ pub const Property = union(PropertyIdTag) { /// Returns the given longhand property for a shorthand. pub fn longhand(this: *const Property, property_id: *const PropertyId) ?Property { - _ = property_id; // autofix switch (this.*) { + .@"background-position" => |*v| return v.longhand(property_id), + .overflow => |*v| return v.longhand(property_id), + .@"inset-block" => |*v| return v.longhand(property_id), + .@"inset-inline" => |*v| return v.longhand(property_id), + .inset => |*v| return v.longhand(property_id), + .@"border-radius" => |*v| { + if (!v[1].eq(property_id.prefix())) return null; + return v[0].longhand(property_id); + }, + .@"border-image" => |*v| { + if (!v[1].eq(property_id.prefix())) return null; + return v[0].longhand(property_id); + }, + .@"border-color" => |*v| return v.longhand(property_id), + .@"border-style" => |*v| return v.longhand(property_id), + .@"border-width" => |*v| return v.longhand(property_id), + .@"border-block-color" => |*v| return v.longhand(property_id), + .@"border-block-style" => |*v| return v.longhand(property_id), + .@"border-block-width" => |*v| return v.longhand(property_id), + .@"border-inline-color" => |*v| return v.longhand(property_id), + .@"border-inline-style" => |*v| return v.longhand(property_id), + .@"border-inline-width" => |*v| return v.longhand(property_id), + .border => |*v| return v.longhand(property_id), + .@"border-top" => |*v| return v.longhand(property_id), + .@"border-bottom" => |*v| return v.longhand(property_id), + .@"border-left" => |*v| return v.longhand(property_id), + .@"border-right" => |*v| return v.longhand(property_id), + .@"border-block" => |*v| return v.longhand(property_id), + .@"border-block-start" => |*v| return v.longhand(property_id), + .@"border-block-end" => |*v| return v.longhand(property_id), + .@"border-inline" => |*v| return v.longhand(property_id), + .@"border-inline-start" => |*v| return v.longhand(property_id), + .@"border-inline-end" => |*v| return v.longhand(property_id), + .outline => |*v| return v.longhand(property_id), + .@"flex-flow" => |*v| { + if (!v[1].eq(property_id.prefix())) return null; + return v[0].longhand(property_id); + }, + .flex => |*v| { + if (!v[1].eq(property_id.prefix())) return null; + return v[0].longhand(property_id); + }, + .@"place-content" => |*v| return v.longhand(property_id), + .@"place-self" => |*v| return v.longhand(property_id), + .@"place-items" => |*v| return v.longhand(property_id), + .gap => |*v| return v.longhand(property_id), + .@"margin-block" => |*v| return v.longhand(property_id), + .@"margin-inline" => |*v| return v.longhand(property_id), + .margin => |*v| return v.longhand(property_id), + .@"padding-block" => |*v| return v.longhand(property_id), + .@"padding-inline" => |*v| return v.longhand(property_id), + .padding => |*v| return v.longhand(property_id), + .@"scroll-margin-block" => |*v| return v.longhand(property_id), + .@"scroll-margin-inline" => |*v| return v.longhand(property_id), + .@"scroll-margin" => |*v| return v.longhand(property_id), + .@"scroll-padding-block" => |*v| return v.longhand(property_id), + .@"scroll-padding-inline" => |*v| return v.longhand(property_id), + .@"scroll-padding" => |*v| return v.longhand(property_id), + .font => |*v| return v.longhand(property_id), + .mask => |*v| { + if (!v[1].eq(property_id.prefix())) return null; + return v[0].longhand(property_id); + }, + .@"mask-border" => |*v| return v.longhand(property_id), else => {}, } return null; } + + pub fn eql(lhs: *const Property, rhs: *const Property) bool { + if (@intFromEnum(lhs.*) != @intFromEnum(rhs.*)) return false; + return switch (lhs.*) { + .@"background-color" => |*v| css.generic.eql(CssColor, v, &rhs.@"background-color"), + .@"background-image" => |*v| css.generic.eql(SmallList(Image, 1), v, &rhs.@"background-image"), + .@"background-position-x" => |*v| css.generic.eql(SmallList(css_values.position.HorizontalPosition, 1), v, &rhs.@"background-position-x"), + .@"background-position-y" => |*v| css.generic.eql(SmallList(css_values.position.HorizontalPosition, 1), v, &rhs.@"background-position-y"), + .@"background-position" => |*v| css.generic.eql(SmallList(background.BackgroundPosition, 1), v, &rhs.@"background-position"), + .@"background-size" => |*v| css.generic.eql(SmallList(background.BackgroundSize, 1), v, &rhs.@"background-size"), + .@"background-repeat" => |*v| css.generic.eql(SmallList(background.BackgroundSize, 1), v, &rhs.@"background-repeat"), + .@"background-attachment" => |*v| css.generic.eql(SmallList(background.BackgroundAttachment, 1), v, &rhs.@"background-attachment"), + .@"background-clip" => |*v| css.generic.eql(SmallList(background.BackgroundAttachment, 1), &v[0], &v[0]) and v[1].eq(rhs.@"background-clip"[1]), + .@"background-origin" => |*v| css.generic.eql(SmallList(background.BackgroundOrigin, 1), v, &rhs.@"background-origin"), + .background => |*v| css.generic.eql(SmallList(background.Background, 1), v, &rhs.background), + .@"box-shadow" => |*v| css.generic.eql(SmallList(box_shadow.BoxShadow, 1), &v[0], &v[0]) and v[1].eq(rhs.@"box-shadow"[1]), + .opacity => |*v| css.generic.eql(css.css_values.alpha.AlphaValue, v, &rhs.opacity), + .color => |*v| css.generic.eql(CssColor, v, &rhs.color), + .display => |*v| css.generic.eql(display.Display, v, &rhs.display), + .visibility => |*v| css.generic.eql(display.Visibility, v, &rhs.visibility), + .width => |*v| css.generic.eql(size.Size, v, &rhs.width), + .height => |*v| css.generic.eql(size.Size, v, &rhs.height), + .@"min-width" => |*v| css.generic.eql(size.Size, v, &rhs.@"min-width"), + .@"min-height" => |*v| css.generic.eql(size.Size, v, &rhs.@"min-height"), + .@"max-width" => |*v| css.generic.eql(size.MaxSize, v, &rhs.@"max-width"), + .@"max-height" => |*v| css.generic.eql(size.MaxSize, v, &rhs.@"max-height"), + .@"block-size" => |*v| css.generic.eql(size.Size, v, &rhs.@"block-size"), + .@"inline-size" => |*v| css.generic.eql(size.Size, v, &rhs.@"inline-size"), + .@"min-block-size" => |*v| css.generic.eql(size.Size, v, &rhs.@"min-block-size"), + .@"min-inline-size" => |*v| css.generic.eql(size.Size, v, &rhs.@"min-inline-size"), + .@"max-block-size" => |*v| css.generic.eql(size.MaxSize, v, &rhs.@"max-block-size"), + .@"max-inline-size" => |*v| css.generic.eql(size.MaxSize, v, &rhs.@"max-inline-size"), + .@"box-sizing" => |*v| css.generic.eql(size.BoxSizing, &v[0], &v[0]) and v[1].eq(rhs.@"box-sizing"[1]), + .@"aspect-ratio" => |*v| css.generic.eql(size.AspectRatio, v, &rhs.@"aspect-ratio"), + .overflow => |*v| css.generic.eql(overflow.Overflow, v, &rhs.overflow), + .@"overflow-x" => |*v| css.generic.eql(overflow.OverflowKeyword, v, &rhs.@"overflow-x"), + .@"overflow-y" => |*v| css.generic.eql(overflow.OverflowKeyword, v, &rhs.@"overflow-y"), + .@"text-overflow" => |*v| css.generic.eql(overflow.TextOverflow, &v[0], &v[0]) and v[1].eq(rhs.@"text-overflow"[1]), + .position => |*v| css.generic.eql(position.Position, v, &rhs.position), + .top => |*v| css.generic.eql(LengthPercentageOrAuto, v, &rhs.top), + .bottom => |*v| css.generic.eql(LengthPercentageOrAuto, v, &rhs.bottom), + .left => |*v| css.generic.eql(LengthPercentageOrAuto, v, &rhs.left), + .right => |*v| css.generic.eql(LengthPercentageOrAuto, v, &rhs.right), + .@"inset-block-start" => |*v| css.generic.eql(LengthPercentageOrAuto, v, &rhs.@"inset-block-start"), + .@"inset-block-end" => |*v| css.generic.eql(LengthPercentageOrAuto, v, &rhs.@"inset-block-end"), + .@"inset-inline-start" => |*v| css.generic.eql(LengthPercentageOrAuto, v, &rhs.@"inset-inline-start"), + .@"inset-inline-end" => |*v| css.generic.eql(LengthPercentageOrAuto, v, &rhs.@"inset-inline-end"), + .@"inset-block" => |*v| css.generic.eql(margin_padding.InsetBlock, v, &rhs.@"inset-block"), + .@"inset-inline" => |*v| css.generic.eql(margin_padding.InsetInline, v, &rhs.@"inset-inline"), + .inset => |*v| css.generic.eql(margin_padding.Inset, v, &rhs.inset), + .@"border-spacing" => |*v| css.generic.eql(css.css_values.size.Size2D(Length), v, &rhs.@"border-spacing"), + .@"border-top-color" => |*v| css.generic.eql(CssColor, v, &rhs.@"border-top-color"), + .@"border-bottom-color" => |*v| css.generic.eql(CssColor, v, &rhs.@"border-bottom-color"), + .@"border-left-color" => |*v| css.generic.eql(CssColor, v, &rhs.@"border-left-color"), + .@"border-right-color" => |*v| css.generic.eql(CssColor, v, &rhs.@"border-right-color"), + .@"border-block-start-color" => |*v| css.generic.eql(CssColor, v, &rhs.@"border-block-start-color"), + .@"border-block-end-color" => |*v| css.generic.eql(CssColor, v, &rhs.@"border-block-end-color"), + .@"border-inline-start-color" => |*v| css.generic.eql(CssColor, v, &rhs.@"border-inline-start-color"), + .@"border-inline-end-color" => |*v| css.generic.eql(CssColor, v, &rhs.@"border-inline-end-color"), + .@"border-top-style" => |*v| css.generic.eql(border.LineStyle, v, &rhs.@"border-top-style"), + .@"border-bottom-style" => |*v| css.generic.eql(border.LineStyle, v, &rhs.@"border-bottom-style"), + .@"border-left-style" => |*v| css.generic.eql(border.LineStyle, v, &rhs.@"border-left-style"), + .@"border-right-style" => |*v| css.generic.eql(border.LineStyle, v, &rhs.@"border-right-style"), + .@"border-block-start-style" => |*v| css.generic.eql(border.LineStyle, v, &rhs.@"border-block-start-style"), + .@"border-block-end-style" => |*v| css.generic.eql(border.LineStyle, v, &rhs.@"border-block-end-style"), + .@"border-inline-start-style" => |*v| css.generic.eql(border.LineStyle, v, &rhs.@"border-inline-start-style"), + .@"border-inline-end-style" => |*v| css.generic.eql(border.LineStyle, v, &rhs.@"border-inline-end-style"), + .@"border-top-width" => |*v| css.generic.eql(BorderSideWidth, v, &rhs.@"border-top-width"), + .@"border-bottom-width" => |*v| css.generic.eql(BorderSideWidth, v, &rhs.@"border-bottom-width"), + .@"border-left-width" => |*v| css.generic.eql(BorderSideWidth, v, &rhs.@"border-left-width"), + .@"border-right-width" => |*v| css.generic.eql(BorderSideWidth, v, &rhs.@"border-right-width"), + .@"border-block-start-width" => |*v| css.generic.eql(BorderSideWidth, v, &rhs.@"border-block-start-width"), + .@"border-block-end-width" => |*v| css.generic.eql(BorderSideWidth, v, &rhs.@"border-block-end-width"), + .@"border-inline-start-width" => |*v| css.generic.eql(BorderSideWidth, v, &rhs.@"border-inline-start-width"), + .@"border-inline-end-width" => |*v| css.generic.eql(BorderSideWidth, v, &rhs.@"border-inline-end-width"), + .@"border-top-left-radius" => |*v| css.generic.eql(Size2D(LengthPercentage), &v[0], &v[0]) and v[1].eq(rhs.@"border-top-left-radius"[1]), + .@"border-top-right-radius" => |*v| css.generic.eql(Size2D(LengthPercentage), &v[0], &v[0]) and v[1].eq(rhs.@"border-top-right-radius"[1]), + .@"border-bottom-left-radius" => |*v| css.generic.eql(Size2D(LengthPercentage), &v[0], &v[0]) and v[1].eq(rhs.@"border-bottom-left-radius"[1]), + .@"border-bottom-right-radius" => |*v| css.generic.eql(Size2D(LengthPercentage), &v[0], &v[0]) and v[1].eq(rhs.@"border-bottom-right-radius"[1]), + .@"border-start-start-radius" => |*v| css.generic.eql(Size2D(LengthPercentage), v, &rhs.@"border-start-start-radius"), + .@"border-start-end-radius" => |*v| css.generic.eql(Size2D(LengthPercentage), v, &rhs.@"border-start-end-radius"), + .@"border-end-start-radius" => |*v| css.generic.eql(Size2D(LengthPercentage), v, &rhs.@"border-end-start-radius"), + .@"border-end-end-radius" => |*v| css.generic.eql(Size2D(LengthPercentage), v, &rhs.@"border-end-end-radius"), + .@"border-radius" => |*v| css.generic.eql(BorderRadius, &v[0], &v[0]) and v[1].eq(rhs.@"border-radius"[1]), + .@"border-image-source" => |*v| css.generic.eql(Image, v, &rhs.@"border-image-source"), + .@"border-image-outset" => |*v| css.generic.eql(Rect(LengthOrNumber), v, &rhs.@"border-image-outset"), + .@"border-image-repeat" => |*v| css.generic.eql(BorderImageRepeat, v, &rhs.@"border-image-repeat"), + .@"border-image-width" => |*v| css.generic.eql(Rect(BorderImageSideWidth), v, &rhs.@"border-image-width"), + .@"border-image-slice" => |*v| css.generic.eql(BorderImageSlice, v, &rhs.@"border-image-slice"), + .@"border-image" => |*v| css.generic.eql(BorderImage, &v[0], &v[0]) and v[1].eq(rhs.@"border-image"[1]), + .@"border-color" => |*v| css.generic.eql(BorderColor, v, &rhs.@"border-color"), + .@"border-style" => |*v| css.generic.eql(BorderStyle, v, &rhs.@"border-style"), + .@"border-width" => |*v| css.generic.eql(BorderWidth, v, &rhs.@"border-width"), + .@"border-block-color" => |*v| css.generic.eql(BorderBlockColor, v, &rhs.@"border-block-color"), + .@"border-block-style" => |*v| css.generic.eql(BorderBlockStyle, v, &rhs.@"border-block-style"), + .@"border-block-width" => |*v| css.generic.eql(BorderBlockWidth, v, &rhs.@"border-block-width"), + .@"border-inline-color" => |*v| css.generic.eql(BorderInlineColor, v, &rhs.@"border-inline-color"), + .@"border-inline-style" => |*v| css.generic.eql(BorderInlineStyle, v, &rhs.@"border-inline-style"), + .@"border-inline-width" => |*v| css.generic.eql(BorderInlineWidth, v, &rhs.@"border-inline-width"), + .border => |*v| css.generic.eql(Border, v, &rhs.border), + .@"border-top" => |*v| css.generic.eql(BorderTop, v, &rhs.@"border-top"), + .@"border-bottom" => |*v| css.generic.eql(BorderBottom, v, &rhs.@"border-bottom"), + .@"border-left" => |*v| css.generic.eql(BorderLeft, v, &rhs.@"border-left"), + .@"border-right" => |*v| css.generic.eql(BorderRight, v, &rhs.@"border-right"), + .@"border-block" => |*v| css.generic.eql(BorderBlock, v, &rhs.@"border-block"), + .@"border-block-start" => |*v| css.generic.eql(BorderBlockStart, v, &rhs.@"border-block-start"), + .@"border-block-end" => |*v| css.generic.eql(BorderBlockEnd, v, &rhs.@"border-block-end"), + .@"border-inline" => |*v| css.generic.eql(BorderInline, v, &rhs.@"border-inline"), + .@"border-inline-start" => |*v| css.generic.eql(BorderInlineStart, v, &rhs.@"border-inline-start"), + .@"border-inline-end" => |*v| css.generic.eql(BorderInlineEnd, v, &rhs.@"border-inline-end"), + .outline => |*v| css.generic.eql(Outline, v, &rhs.outline), + .@"outline-color" => |*v| css.generic.eql(CssColor, v, &rhs.@"outline-color"), + .@"outline-style" => |*v| css.generic.eql(OutlineStyle, v, &rhs.@"outline-style"), + .@"outline-width" => |*v| css.generic.eql(BorderSideWidth, v, &rhs.@"outline-width"), + .@"flex-direction" => |*v| css.generic.eql(FlexDirection, &v[0], &v[0]) and v[1].eq(rhs.@"flex-direction"[1]), + .@"flex-wrap" => |*v| css.generic.eql(FlexWrap, &v[0], &v[0]) and v[1].eq(rhs.@"flex-wrap"[1]), + .@"flex-flow" => |*v| css.generic.eql(FlexFlow, &v[0], &v[0]) and v[1].eq(rhs.@"flex-flow"[1]), + .@"flex-grow" => |*v| css.generic.eql(CSSNumber, &v[0], &v[0]) and v[1].eq(rhs.@"flex-grow"[1]), + .@"flex-shrink" => |*v| css.generic.eql(CSSNumber, &v[0], &v[0]) and v[1].eq(rhs.@"flex-shrink"[1]), + .@"flex-basis" => |*v| css.generic.eql(LengthPercentageOrAuto, &v[0], &v[0]) and v[1].eq(rhs.@"flex-basis"[1]), + .flex => |*v| css.generic.eql(Flex, &v[0], &v[0]) and v[1].eq(rhs.flex[1]), + .order => |*v| css.generic.eql(CSSInteger, &v[0], &v[0]) and v[1].eq(rhs.order[1]), + .@"align-content" => |*v| css.generic.eql(AlignContent, &v[0], &v[0]) and v[1].eq(rhs.@"align-content"[1]), + .@"justify-content" => |*v| css.generic.eql(JustifyContent, &v[0], &v[0]) and v[1].eq(rhs.@"justify-content"[1]), + .@"place-content" => |*v| css.generic.eql(PlaceContent, v, &rhs.@"place-content"), + .@"align-self" => |*v| css.generic.eql(AlignSelf, &v[0], &v[0]) and v[1].eq(rhs.@"align-self"[1]), + .@"justify-self" => |*v| css.generic.eql(JustifySelf, v, &rhs.@"justify-self"), + .@"place-self" => |*v| css.generic.eql(PlaceSelf, v, &rhs.@"place-self"), + .@"align-items" => |*v| css.generic.eql(AlignItems, &v[0], &v[0]) and v[1].eq(rhs.@"align-items"[1]), + .@"justify-items" => |*v| css.generic.eql(JustifyItems, v, &rhs.@"justify-items"), + .@"place-items" => |*v| css.generic.eql(PlaceItems, v, &rhs.@"place-items"), + .@"row-gap" => |*v| css.generic.eql(GapValue, v, &rhs.@"row-gap"), + .@"column-gap" => |*v| css.generic.eql(GapValue, v, &rhs.@"column-gap"), + .gap => |*v| css.generic.eql(Gap, v, &rhs.gap), + .@"box-orient" => |*v| css.generic.eql(BoxOrient, &v[0], &v[0]) and v[1].eq(rhs.@"box-orient"[1]), + .@"box-direction" => |*v| css.generic.eql(BoxDirection, &v[0], &v[0]) and v[1].eq(rhs.@"box-direction"[1]), + .@"box-ordinal-group" => |*v| css.generic.eql(CSSInteger, &v[0], &v[0]) and v[1].eq(rhs.@"box-ordinal-group"[1]), + .@"box-align" => |*v| css.generic.eql(BoxAlign, &v[0], &v[0]) and v[1].eq(rhs.@"box-align"[1]), + .@"box-flex" => |*v| css.generic.eql(CSSNumber, &v[0], &v[0]) and v[1].eq(rhs.@"box-flex"[1]), + .@"box-flex-group" => |*v| css.generic.eql(CSSInteger, &v[0], &v[0]) and v[1].eq(rhs.@"box-flex-group"[1]), + .@"box-pack" => |*v| css.generic.eql(BoxPack, &v[0], &v[0]) and v[1].eq(rhs.@"box-pack"[1]), + .@"box-lines" => |*v| css.generic.eql(BoxLines, &v[0], &v[0]) and v[1].eq(rhs.@"box-lines"[1]), + .@"flex-pack" => |*v| css.generic.eql(FlexPack, &v[0], &v[0]) and v[1].eq(rhs.@"flex-pack"[1]), + .@"flex-order" => |*v| css.generic.eql(CSSInteger, &v[0], &v[0]) and v[1].eq(rhs.@"flex-order"[1]), + .@"flex-align" => |*v| css.generic.eql(BoxAlign, &v[0], &v[0]) and v[1].eq(rhs.@"flex-align"[1]), + .@"flex-item-align" => |*v| css.generic.eql(FlexItemAlign, &v[0], &v[0]) and v[1].eq(rhs.@"flex-item-align"[1]), + .@"flex-line-pack" => |*v| css.generic.eql(FlexLinePack, &v[0], &v[0]) and v[1].eq(rhs.@"flex-line-pack"[1]), + .@"flex-positive" => |*v| css.generic.eql(CSSNumber, &v[0], &v[0]) and v[1].eq(rhs.@"flex-positive"[1]), + .@"flex-negative" => |*v| css.generic.eql(CSSNumber, &v[0], &v[0]) and v[1].eq(rhs.@"flex-negative"[1]), + .@"flex-preferred-size" => |*v| css.generic.eql(LengthPercentageOrAuto, &v[0], &v[0]) and v[1].eq(rhs.@"flex-preferred-size"[1]), + .@"margin-top" => |*v| css.generic.eql(LengthPercentageOrAuto, v, &rhs.@"margin-top"), + .@"margin-bottom" => |*v| css.generic.eql(LengthPercentageOrAuto, v, &rhs.@"margin-bottom"), + .@"margin-left" => |*v| css.generic.eql(LengthPercentageOrAuto, v, &rhs.@"margin-left"), + .@"margin-right" => |*v| css.generic.eql(LengthPercentageOrAuto, v, &rhs.@"margin-right"), + .@"margin-block-start" => |*v| css.generic.eql(LengthPercentageOrAuto, v, &rhs.@"margin-block-start"), + .@"margin-block-end" => |*v| css.generic.eql(LengthPercentageOrAuto, v, &rhs.@"margin-block-end"), + .@"margin-inline-start" => |*v| css.generic.eql(LengthPercentageOrAuto, v, &rhs.@"margin-inline-start"), + .@"margin-inline-end" => |*v| css.generic.eql(LengthPercentageOrAuto, v, &rhs.@"margin-inline-end"), + .@"margin-block" => |*v| css.generic.eql(MarginBlock, v, &rhs.@"margin-block"), + .@"margin-inline" => |*v| css.generic.eql(MarginInline, v, &rhs.@"margin-inline"), + .margin => |*v| css.generic.eql(Margin, v, &rhs.margin), + .@"padding-top" => |*v| css.generic.eql(LengthPercentageOrAuto, v, &rhs.@"padding-top"), + .@"padding-bottom" => |*v| css.generic.eql(LengthPercentageOrAuto, v, &rhs.@"padding-bottom"), + .@"padding-left" => |*v| css.generic.eql(LengthPercentageOrAuto, v, &rhs.@"padding-left"), + .@"padding-right" => |*v| css.generic.eql(LengthPercentageOrAuto, v, &rhs.@"padding-right"), + .@"padding-block-start" => |*v| css.generic.eql(LengthPercentageOrAuto, v, &rhs.@"padding-block-start"), + .@"padding-block-end" => |*v| css.generic.eql(LengthPercentageOrAuto, v, &rhs.@"padding-block-end"), + .@"padding-inline-start" => |*v| css.generic.eql(LengthPercentageOrAuto, v, &rhs.@"padding-inline-start"), + .@"padding-inline-end" => |*v| css.generic.eql(LengthPercentageOrAuto, v, &rhs.@"padding-inline-end"), + .@"padding-block" => |*v| css.generic.eql(PaddingBlock, v, &rhs.@"padding-block"), + .@"padding-inline" => |*v| css.generic.eql(PaddingInline, v, &rhs.@"padding-inline"), + .padding => |*v| css.generic.eql(Padding, v, &rhs.padding), + .@"scroll-margin-top" => |*v| css.generic.eql(LengthPercentageOrAuto, v, &rhs.@"scroll-margin-top"), + .@"scroll-margin-bottom" => |*v| css.generic.eql(LengthPercentageOrAuto, v, &rhs.@"scroll-margin-bottom"), + .@"scroll-margin-left" => |*v| css.generic.eql(LengthPercentageOrAuto, v, &rhs.@"scroll-margin-left"), + .@"scroll-margin-right" => |*v| css.generic.eql(LengthPercentageOrAuto, v, &rhs.@"scroll-margin-right"), + .@"scroll-margin-block-start" => |*v| css.generic.eql(LengthPercentageOrAuto, v, &rhs.@"scroll-margin-block-start"), + .@"scroll-margin-block-end" => |*v| css.generic.eql(LengthPercentageOrAuto, v, &rhs.@"scroll-margin-block-end"), + .@"scroll-margin-inline-start" => |*v| css.generic.eql(LengthPercentageOrAuto, v, &rhs.@"scroll-margin-inline-start"), + .@"scroll-margin-inline-end" => |*v| css.generic.eql(LengthPercentageOrAuto, v, &rhs.@"scroll-margin-inline-end"), + .@"scroll-margin-block" => |*v| css.generic.eql(ScrollMarginBlock, v, &rhs.@"scroll-margin-block"), + .@"scroll-margin-inline" => |*v| css.generic.eql(ScrollMarginInline, v, &rhs.@"scroll-margin-inline"), + .@"scroll-margin" => |*v| css.generic.eql(ScrollMargin, v, &rhs.@"scroll-margin"), + .@"scroll-padding-top" => |*v| css.generic.eql(LengthPercentageOrAuto, v, &rhs.@"scroll-padding-top"), + .@"scroll-padding-bottom" => |*v| css.generic.eql(LengthPercentageOrAuto, v, &rhs.@"scroll-padding-bottom"), + .@"scroll-padding-left" => |*v| css.generic.eql(LengthPercentageOrAuto, v, &rhs.@"scroll-padding-left"), + .@"scroll-padding-right" => |*v| css.generic.eql(LengthPercentageOrAuto, v, &rhs.@"scroll-padding-right"), + .@"scroll-padding-block-start" => |*v| css.generic.eql(LengthPercentageOrAuto, v, &rhs.@"scroll-padding-block-start"), + .@"scroll-padding-block-end" => |*v| css.generic.eql(LengthPercentageOrAuto, v, &rhs.@"scroll-padding-block-end"), + .@"scroll-padding-inline-start" => |*v| css.generic.eql(LengthPercentageOrAuto, v, &rhs.@"scroll-padding-inline-start"), + .@"scroll-padding-inline-end" => |*v| css.generic.eql(LengthPercentageOrAuto, v, &rhs.@"scroll-padding-inline-end"), + .@"scroll-padding-block" => |*v| css.generic.eql(ScrollPaddingBlock, v, &rhs.@"scroll-padding-block"), + .@"scroll-padding-inline" => |*v| css.generic.eql(ScrollPaddingInline, v, &rhs.@"scroll-padding-inline"), + .@"scroll-padding" => |*v| css.generic.eql(ScrollPadding, v, &rhs.@"scroll-padding"), + .@"font-weight" => |*v| css.generic.eql(FontWeight, v, &rhs.@"font-weight"), + .@"font-size" => |*v| css.generic.eql(FontSize, v, &rhs.@"font-size"), + .@"font-stretch" => |*v| css.generic.eql(FontStretch, v, &rhs.@"font-stretch"), + .@"font-family" => |*v| css.generic.eql(BabyList(FontFamily), v, &rhs.@"font-family"), + .@"font-style" => |*v| css.generic.eql(FontStyle, v, &rhs.@"font-style"), + .@"font-variant-caps" => |*v| css.generic.eql(FontVariantCaps, v, &rhs.@"font-variant-caps"), + .@"line-height" => |*v| css.generic.eql(LineHeight, v, &rhs.@"line-height"), + .font => |*v| css.generic.eql(Font, v, &rhs.font), + .@"text-decoration-color" => |*v| css.generic.eql(CssColor, &v[0], &v[0]) and v[1].eq(rhs.@"text-decoration-color"[1]), + .@"text-emphasis-color" => |*v| css.generic.eql(CssColor, &v[0], &v[0]) and v[1].eq(rhs.@"text-emphasis-color"[1]), + .direction => |*v| css.generic.eql(Direction, v, &rhs.direction), + .composes => |*v| css.generic.eql(Composes, v, &rhs.composes), + .@"mask-image" => |*v| css.generic.eql(SmallList(Image, 1), &v[0], &v[0]) and v[1].eq(rhs.@"mask-image"[1]), + .@"mask-mode" => |*v| css.generic.eql(SmallList(MaskMode, 1), v, &rhs.@"mask-mode"), + .@"mask-repeat" => |*v| css.generic.eql(SmallList(BackgroundRepeat, 1), &v[0], &v[0]) and v[1].eq(rhs.@"mask-repeat"[1]), + .@"mask-position-x" => |*v| css.generic.eql(SmallList(HorizontalPosition, 1), v, &rhs.@"mask-position-x"), + .@"mask-position-y" => |*v| css.generic.eql(SmallList(VerticalPosition, 1), v, &rhs.@"mask-position-y"), + .@"mask-position" => |*v| css.generic.eql(SmallList(Position, 1), &v[0], &v[0]) and v[1].eq(rhs.@"mask-position"[1]), + .@"mask-clip" => |*v| css.generic.eql(SmallList(MaskClip, 1), &v[0], &v[0]) and v[1].eq(rhs.@"mask-clip"[1]), + .@"mask-origin" => |*v| css.generic.eql(SmallList(GeometryBox, 1), &v[0], &v[0]) and v[1].eq(rhs.@"mask-origin"[1]), + .@"mask-size" => |*v| css.generic.eql(SmallList(BackgroundSize, 1), &v[0], &v[0]) and v[1].eq(rhs.@"mask-size"[1]), + .@"mask-composite" => |*v| css.generic.eql(SmallList(MaskComposite, 1), v, &rhs.@"mask-composite"), + .@"mask-type" => |*v| css.generic.eql(MaskType, v, &rhs.@"mask-type"), + .mask => |*v| css.generic.eql(SmallList(Mask, 1), &v[0], &v[0]) and v[1].eq(rhs.mask[1]), + .@"mask-border-source" => |*v| css.generic.eql(Image, v, &rhs.@"mask-border-source"), + .@"mask-border-mode" => |*v| css.generic.eql(MaskBorderMode, v, &rhs.@"mask-border-mode"), + .@"mask-border-slice" => |*v| css.generic.eql(BorderImageSlice, v, &rhs.@"mask-border-slice"), + .@"mask-border-width" => |*v| css.generic.eql(Rect(BorderImageSideWidth), v, &rhs.@"mask-border-width"), + .@"mask-border-outset" => |*v| css.generic.eql(Rect(LengthOrNumber), v, &rhs.@"mask-border-outset"), + .@"mask-border-repeat" => |*v| css.generic.eql(BorderImageRepeat, v, &rhs.@"mask-border-repeat"), + .@"mask-border" => |*v| css.generic.eql(MaskBorder, v, &rhs.@"mask-border"), + .@"-webkit-mask-composite" => |*v| css.generic.eql(SmallList(WebKitMaskComposite, 1), v, &rhs.@"-webkit-mask-composite"), + .@"mask-source-type" => |*v| css.generic.eql(SmallList(WebKitMaskSourceType, 1), &v[0], &v[0]) and v[1].eq(rhs.@"mask-source-type"[1]), + .@"mask-box-image" => |*v| css.generic.eql(BorderImage, &v[0], &v[0]) and v[1].eq(rhs.@"mask-box-image"[1]), + .@"mask-box-image-source" => |*v| css.generic.eql(Image, &v[0], &v[0]) and v[1].eq(rhs.@"mask-box-image-source"[1]), + .@"mask-box-image-slice" => |*v| css.generic.eql(BorderImageSlice, &v[0], &v[0]) and v[1].eq(rhs.@"mask-box-image-slice"[1]), + .@"mask-box-image-width" => |*v| css.generic.eql(Rect(BorderImageSideWidth), &v[0], &v[0]) and v[1].eq(rhs.@"mask-box-image-width"[1]), + .@"mask-box-image-outset" => |*v| css.generic.eql(Rect(LengthOrNumber), &v[0], &v[0]) and v[1].eq(rhs.@"mask-box-image-outset"[1]), + .@"mask-box-image-repeat" => |*v| css.generic.eql(BorderImageRepeat, &v[0], &v[0]) and v[1].eq(rhs.@"mask-box-image-repeat"[1]), + .all, .unparsed => true, + .custom => |*c| c.eql(&rhs.custom), + }; + } }; pub const PropertyId = union(PropertyIdTag) { @"background-color", + @"background-image", + @"background-position-x", + @"background-position-y", + @"background-position", + @"background-size", + @"background-repeat", + @"background-attachment", + @"background-clip": VendorPrefix, + @"background-origin", + background, + @"box-shadow": VendorPrefix, + opacity, color, + display, + visibility, + width, + height, + @"min-width", + @"min-height", + @"max-width", + @"max-height", + @"block-size", + @"inline-size", + @"min-block-size", + @"min-inline-size", + @"max-block-size", + @"max-inline-size", + @"box-sizing": VendorPrefix, + @"aspect-ratio", + overflow, + @"overflow-x", + @"overflow-y", + @"text-overflow": VendorPrefix, + position, + top, + bottom, + left, + right, + @"inset-block-start", + @"inset-block-end", + @"inset-inline-start", + @"inset-inline-end", + @"inset-block", + @"inset-inline", + inset, @"border-spacing", @"border-top-color", @"border-bottom-color", @@ -574,14 +6988,174 @@ pub const PropertyId = union(PropertyIdTag) { @"border-right-style", @"border-block-start-style", @"border-block-end-style", + @"border-inline-start-style", + @"border-inline-end-style", @"border-top-width", @"border-bottom-width", @"border-left-width", @"border-right-width", + @"border-block-start-width", + @"border-block-end-width", + @"border-inline-start-width", + @"border-inline-end-width", + @"border-top-left-radius": VendorPrefix, + @"border-top-right-radius": VendorPrefix, + @"border-bottom-left-radius": VendorPrefix, + @"border-bottom-right-radius": VendorPrefix, + @"border-start-start-radius", + @"border-start-end-radius", + @"border-end-start-radius", + @"border-end-end-radius", + @"border-radius": VendorPrefix, + @"border-image-source", + @"border-image-outset", + @"border-image-repeat", + @"border-image-width", + @"border-image-slice", + @"border-image": VendorPrefix, + @"border-color", + @"border-style", + @"border-width", + @"border-block-color", + @"border-block-style", + @"border-block-width", + @"border-inline-color", + @"border-inline-style", + @"border-inline-width", + border, + @"border-top", + @"border-bottom", + @"border-left", + @"border-right", + @"border-block", + @"border-block-start", + @"border-block-end", + @"border-inline", + @"border-inline-start", + @"border-inline-end", + outline, @"outline-color", + @"outline-style", + @"outline-width", + @"flex-direction": VendorPrefix, + @"flex-wrap": VendorPrefix, + @"flex-flow": VendorPrefix, + @"flex-grow": VendorPrefix, + @"flex-shrink": VendorPrefix, + @"flex-basis": VendorPrefix, + flex: VendorPrefix, + order: VendorPrefix, + @"align-content": VendorPrefix, + @"justify-content": VendorPrefix, + @"place-content", + @"align-self": VendorPrefix, + @"justify-self", + @"place-self", + @"align-items": VendorPrefix, + @"justify-items", + @"place-items", + @"row-gap", + @"column-gap", + gap, + @"box-orient": VendorPrefix, + @"box-direction": VendorPrefix, + @"box-ordinal-group": VendorPrefix, + @"box-align": VendorPrefix, + @"box-flex": VendorPrefix, + @"box-flex-group": VendorPrefix, + @"box-pack": VendorPrefix, + @"box-lines": VendorPrefix, + @"flex-pack": VendorPrefix, + @"flex-order": VendorPrefix, + @"flex-align": VendorPrefix, + @"flex-item-align": VendorPrefix, + @"flex-line-pack": VendorPrefix, + @"flex-positive": VendorPrefix, + @"flex-negative": VendorPrefix, + @"flex-preferred-size": VendorPrefix, + @"margin-top", + @"margin-bottom", + @"margin-left", + @"margin-right", + @"margin-block-start", + @"margin-block-end", + @"margin-inline-start", + @"margin-inline-end", + @"margin-block", + @"margin-inline", + margin, + @"padding-top", + @"padding-bottom", + @"padding-left", + @"padding-right", + @"padding-block-start", + @"padding-block-end", + @"padding-inline-start", + @"padding-inline-end", + @"padding-block", + @"padding-inline", + padding, + @"scroll-margin-top", + @"scroll-margin-bottom", + @"scroll-margin-left", + @"scroll-margin-right", + @"scroll-margin-block-start", + @"scroll-margin-block-end", + @"scroll-margin-inline-start", + @"scroll-margin-inline-end", + @"scroll-margin-block", + @"scroll-margin-inline", + @"scroll-margin", + @"scroll-padding-top", + @"scroll-padding-bottom", + @"scroll-padding-left", + @"scroll-padding-right", + @"scroll-padding-block-start", + @"scroll-padding-block-end", + @"scroll-padding-inline-start", + @"scroll-padding-inline-end", + @"scroll-padding-block", + @"scroll-padding-inline", + @"scroll-padding", + @"font-weight", + @"font-size", + @"font-stretch", + @"font-family", + @"font-style", + @"font-variant-caps", + @"line-height", + font, @"text-decoration-color": VendorPrefix, @"text-emphasis-color": VendorPrefix, + direction, composes, + @"mask-image": VendorPrefix, + @"mask-mode", + @"mask-repeat": VendorPrefix, + @"mask-position-x", + @"mask-position-y", + @"mask-position": VendorPrefix, + @"mask-clip": VendorPrefix, + @"mask-origin": VendorPrefix, + @"mask-size": VendorPrefix, + @"mask-composite", + @"mask-type", + mask: VendorPrefix, + @"mask-border-source", + @"mask-border-mode", + @"mask-border-slice", + @"mask-border-width", + @"mask-border-outset", + @"mask-border-repeat", + @"mask-border", + @"-webkit-mask-composite", + @"mask-source-type": VendorPrefix, + @"mask-box-image": VendorPrefix, + @"mask-box-image-source": VendorPrefix, + @"mask-box-image-slice": VendorPrefix, + @"mask-box-image-width": VendorPrefix, + @"mask-box-image-outset": VendorPrefix, + @"mask-box-image-repeat": VendorPrefix, all, unparsed, custom: CustomPropertyName, @@ -597,7 +7171,51 @@ pub const PropertyId = union(PropertyIdTag) { pub fn prefix(this: *const PropertyId) VendorPrefix { return switch (this.*) { .@"background-color" => VendorPrefix.empty(), + .@"background-image" => VendorPrefix.empty(), + .@"background-position-x" => VendorPrefix.empty(), + .@"background-position-y" => VendorPrefix.empty(), + .@"background-position" => VendorPrefix.empty(), + .@"background-size" => VendorPrefix.empty(), + .@"background-repeat" => VendorPrefix.empty(), + .@"background-attachment" => VendorPrefix.empty(), + .@"background-clip" => |p| p, + .@"background-origin" => VendorPrefix.empty(), + .background => VendorPrefix.empty(), + .@"box-shadow" => |p| p, + .opacity => VendorPrefix.empty(), .color => VendorPrefix.empty(), + .display => VendorPrefix.empty(), + .visibility => VendorPrefix.empty(), + .width => VendorPrefix.empty(), + .height => VendorPrefix.empty(), + .@"min-width" => VendorPrefix.empty(), + .@"min-height" => VendorPrefix.empty(), + .@"max-width" => VendorPrefix.empty(), + .@"max-height" => VendorPrefix.empty(), + .@"block-size" => VendorPrefix.empty(), + .@"inline-size" => VendorPrefix.empty(), + .@"min-block-size" => VendorPrefix.empty(), + .@"min-inline-size" => VendorPrefix.empty(), + .@"max-block-size" => VendorPrefix.empty(), + .@"max-inline-size" => VendorPrefix.empty(), + .@"box-sizing" => |p| p, + .@"aspect-ratio" => VendorPrefix.empty(), + .overflow => VendorPrefix.empty(), + .@"overflow-x" => VendorPrefix.empty(), + .@"overflow-y" => VendorPrefix.empty(), + .@"text-overflow" => |p| p, + .position => VendorPrefix.empty(), + .top => VendorPrefix.empty(), + .bottom => VendorPrefix.empty(), + .left => VendorPrefix.empty(), + .right => VendorPrefix.empty(), + .@"inset-block-start" => VendorPrefix.empty(), + .@"inset-block-end" => VendorPrefix.empty(), + .@"inset-inline-start" => VendorPrefix.empty(), + .@"inset-inline-end" => VendorPrefix.empty(), + .@"inset-block" => VendorPrefix.empty(), + .@"inset-inline" => VendorPrefix.empty(), + .inset => VendorPrefix.empty(), .@"border-spacing" => VendorPrefix.empty(), .@"border-top-color" => VendorPrefix.empty(), .@"border-bottom-color" => VendorPrefix.empty(), @@ -613,14 +7231,174 @@ pub const PropertyId = union(PropertyIdTag) { .@"border-right-style" => VendorPrefix.empty(), .@"border-block-start-style" => VendorPrefix.empty(), .@"border-block-end-style" => VendorPrefix.empty(), + .@"border-inline-start-style" => VendorPrefix.empty(), + .@"border-inline-end-style" => VendorPrefix.empty(), .@"border-top-width" => VendorPrefix.empty(), .@"border-bottom-width" => VendorPrefix.empty(), .@"border-left-width" => VendorPrefix.empty(), .@"border-right-width" => VendorPrefix.empty(), + .@"border-block-start-width" => VendorPrefix.empty(), + .@"border-block-end-width" => VendorPrefix.empty(), + .@"border-inline-start-width" => VendorPrefix.empty(), + .@"border-inline-end-width" => VendorPrefix.empty(), + .@"border-top-left-radius" => |p| p, + .@"border-top-right-radius" => |p| p, + .@"border-bottom-left-radius" => |p| p, + .@"border-bottom-right-radius" => |p| p, + .@"border-start-start-radius" => VendorPrefix.empty(), + .@"border-start-end-radius" => VendorPrefix.empty(), + .@"border-end-start-radius" => VendorPrefix.empty(), + .@"border-end-end-radius" => VendorPrefix.empty(), + .@"border-radius" => |p| p, + .@"border-image-source" => VendorPrefix.empty(), + .@"border-image-outset" => VendorPrefix.empty(), + .@"border-image-repeat" => VendorPrefix.empty(), + .@"border-image-width" => VendorPrefix.empty(), + .@"border-image-slice" => VendorPrefix.empty(), + .@"border-image" => |p| p, + .@"border-color" => VendorPrefix.empty(), + .@"border-style" => VendorPrefix.empty(), + .@"border-width" => VendorPrefix.empty(), + .@"border-block-color" => VendorPrefix.empty(), + .@"border-block-style" => VendorPrefix.empty(), + .@"border-block-width" => VendorPrefix.empty(), + .@"border-inline-color" => VendorPrefix.empty(), + .@"border-inline-style" => VendorPrefix.empty(), + .@"border-inline-width" => VendorPrefix.empty(), + .border => VendorPrefix.empty(), + .@"border-top" => VendorPrefix.empty(), + .@"border-bottom" => VendorPrefix.empty(), + .@"border-left" => VendorPrefix.empty(), + .@"border-right" => VendorPrefix.empty(), + .@"border-block" => VendorPrefix.empty(), + .@"border-block-start" => VendorPrefix.empty(), + .@"border-block-end" => VendorPrefix.empty(), + .@"border-inline" => VendorPrefix.empty(), + .@"border-inline-start" => VendorPrefix.empty(), + .@"border-inline-end" => VendorPrefix.empty(), + .outline => VendorPrefix.empty(), .@"outline-color" => VendorPrefix.empty(), + .@"outline-style" => VendorPrefix.empty(), + .@"outline-width" => VendorPrefix.empty(), + .@"flex-direction" => |p| p, + .@"flex-wrap" => |p| p, + .@"flex-flow" => |p| p, + .@"flex-grow" => |p| p, + .@"flex-shrink" => |p| p, + .@"flex-basis" => |p| p, + .flex => |p| p, + .order => |p| p, + .@"align-content" => |p| p, + .@"justify-content" => |p| p, + .@"place-content" => VendorPrefix.empty(), + .@"align-self" => |p| p, + .@"justify-self" => VendorPrefix.empty(), + .@"place-self" => VendorPrefix.empty(), + .@"align-items" => |p| p, + .@"justify-items" => VendorPrefix.empty(), + .@"place-items" => VendorPrefix.empty(), + .@"row-gap" => VendorPrefix.empty(), + .@"column-gap" => VendorPrefix.empty(), + .gap => VendorPrefix.empty(), + .@"box-orient" => |p| p, + .@"box-direction" => |p| p, + .@"box-ordinal-group" => |p| p, + .@"box-align" => |p| p, + .@"box-flex" => |p| p, + .@"box-flex-group" => |p| p, + .@"box-pack" => |p| p, + .@"box-lines" => |p| p, + .@"flex-pack" => |p| p, + .@"flex-order" => |p| p, + .@"flex-align" => |p| p, + .@"flex-item-align" => |p| p, + .@"flex-line-pack" => |p| p, + .@"flex-positive" => |p| p, + .@"flex-negative" => |p| p, + .@"flex-preferred-size" => |p| p, + .@"margin-top" => VendorPrefix.empty(), + .@"margin-bottom" => VendorPrefix.empty(), + .@"margin-left" => VendorPrefix.empty(), + .@"margin-right" => VendorPrefix.empty(), + .@"margin-block-start" => VendorPrefix.empty(), + .@"margin-block-end" => VendorPrefix.empty(), + .@"margin-inline-start" => VendorPrefix.empty(), + .@"margin-inline-end" => VendorPrefix.empty(), + .@"margin-block" => VendorPrefix.empty(), + .@"margin-inline" => VendorPrefix.empty(), + .margin => VendorPrefix.empty(), + .@"padding-top" => VendorPrefix.empty(), + .@"padding-bottom" => VendorPrefix.empty(), + .@"padding-left" => VendorPrefix.empty(), + .@"padding-right" => VendorPrefix.empty(), + .@"padding-block-start" => VendorPrefix.empty(), + .@"padding-block-end" => VendorPrefix.empty(), + .@"padding-inline-start" => VendorPrefix.empty(), + .@"padding-inline-end" => VendorPrefix.empty(), + .@"padding-block" => VendorPrefix.empty(), + .@"padding-inline" => VendorPrefix.empty(), + .padding => VendorPrefix.empty(), + .@"scroll-margin-top" => VendorPrefix.empty(), + .@"scroll-margin-bottom" => VendorPrefix.empty(), + .@"scroll-margin-left" => VendorPrefix.empty(), + .@"scroll-margin-right" => VendorPrefix.empty(), + .@"scroll-margin-block-start" => VendorPrefix.empty(), + .@"scroll-margin-block-end" => VendorPrefix.empty(), + .@"scroll-margin-inline-start" => VendorPrefix.empty(), + .@"scroll-margin-inline-end" => VendorPrefix.empty(), + .@"scroll-margin-block" => VendorPrefix.empty(), + .@"scroll-margin-inline" => VendorPrefix.empty(), + .@"scroll-margin" => VendorPrefix.empty(), + .@"scroll-padding-top" => VendorPrefix.empty(), + .@"scroll-padding-bottom" => VendorPrefix.empty(), + .@"scroll-padding-left" => VendorPrefix.empty(), + .@"scroll-padding-right" => VendorPrefix.empty(), + .@"scroll-padding-block-start" => VendorPrefix.empty(), + .@"scroll-padding-block-end" => VendorPrefix.empty(), + .@"scroll-padding-inline-start" => VendorPrefix.empty(), + .@"scroll-padding-inline-end" => VendorPrefix.empty(), + .@"scroll-padding-block" => VendorPrefix.empty(), + .@"scroll-padding-inline" => VendorPrefix.empty(), + .@"scroll-padding" => VendorPrefix.empty(), + .@"font-weight" => VendorPrefix.empty(), + .@"font-size" => VendorPrefix.empty(), + .@"font-stretch" => VendorPrefix.empty(), + .@"font-family" => VendorPrefix.empty(), + .@"font-style" => VendorPrefix.empty(), + .@"font-variant-caps" => VendorPrefix.empty(), + .@"line-height" => VendorPrefix.empty(), + .font => VendorPrefix.empty(), .@"text-decoration-color" => |p| p, .@"text-emphasis-color" => |p| p, + .direction => VendorPrefix.empty(), .composes => VendorPrefix.empty(), + .@"mask-image" => |p| p, + .@"mask-mode" => VendorPrefix.empty(), + .@"mask-repeat" => |p| p, + .@"mask-position-x" => VendorPrefix.empty(), + .@"mask-position-y" => VendorPrefix.empty(), + .@"mask-position" => |p| p, + .@"mask-clip" => |p| p, + .@"mask-origin" => |p| p, + .@"mask-size" => |p| p, + .@"mask-composite" => VendorPrefix.empty(), + .@"mask-type" => VendorPrefix.empty(), + .mask => |p| p, + .@"mask-border-source" => VendorPrefix.empty(), + .@"mask-border-mode" => VendorPrefix.empty(), + .@"mask-border-slice" => VendorPrefix.empty(), + .@"mask-border-width" => VendorPrefix.empty(), + .@"mask-border-outset" => VendorPrefix.empty(), + .@"mask-border-repeat" => VendorPrefix.empty(), + .@"mask-border" => VendorPrefix.empty(), + .@"-webkit-mask-composite" => VendorPrefix.empty(), + .@"mask-source-type" => |p| p, + .@"mask-box-image" => |p| p, + .@"mask-box-image-source" => |p| p, + .@"mask-box-image-slice" => |p| p, + .@"mask-box-image-width" => |p| p, + .@"mask-box-image-outset" => |p| p, + .@"mask-box-image-repeat" => |p| p, .all, .custom, .unparsed => VendorPrefix.empty(), }; } @@ -630,9 +7408,141 @@ pub const PropertyId = union(PropertyIdTag) { if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "background-color")) { const allowed_prefixes = VendorPrefix{ .none = true }; if (allowed_prefixes.contains(pre)) return .@"background-color"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "background-image")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"background-image"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "background-position-x")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"background-position-x"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "background-position-y")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"background-position-y"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "background-position")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"background-position"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "background-size")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"background-size"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "background-repeat")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"background-repeat"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "background-attachment")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"background-attachment"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "background-clip")) { + const allowed_prefixes = VendorPrefix{ .webkit = true, .moz = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"background-clip" = pre }; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "background-origin")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"background-origin"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "background")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .background; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "box-shadow")) { + const allowed_prefixes = VendorPrefix{ .webkit = true, .moz = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"box-shadow" = pre }; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "opacity")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .opacity; } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "color")) { const allowed_prefixes = VendorPrefix{ .none = true }; if (allowed_prefixes.contains(pre)) return .color; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "display")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .display; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "visibility")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .visibility; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "width")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .width; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "height")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .height; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "min-width")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"min-width"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "min-height")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"min-height"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "max-width")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"max-width"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "max-height")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"max-height"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "block-size")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"block-size"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "inline-size")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"inline-size"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "min-block-size")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"min-block-size"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "min-inline-size")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"min-inline-size"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "max-block-size")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"max-block-size"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "max-inline-size")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"max-inline-size"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "box-sizing")) { + const allowed_prefixes = VendorPrefix{ .webkit = true, .moz = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"box-sizing" = pre }; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "aspect-ratio")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"aspect-ratio"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "overflow")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .overflow; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "overflow-x")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"overflow-x"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "overflow-y")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"overflow-y"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "text-overflow")) { + const allowed_prefixes = VendorPrefix{ .o = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"text-overflow" = pre }; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "position")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .position; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "top")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .top; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "bottom")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .bottom; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "left")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .left; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "right")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .right; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "inset-block-start")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"inset-block-start"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "inset-block-end")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"inset-block-end"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "inset-inline-start")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"inset-inline-start"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "inset-inline-end")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"inset-inline-end"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "inset-block")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"inset-block"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "inset-inline")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"inset-inline"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "inset")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .inset; } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-spacing")) { const allowed_prefixes = VendorPrefix{ .none = true }; if (allowed_prefixes.contains(pre)) return .@"border-spacing"; @@ -678,6 +7588,12 @@ pub const PropertyId = union(PropertyIdTag) { } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-block-end-style")) { const allowed_prefixes = VendorPrefix{ .none = true }; if (allowed_prefixes.contains(pre)) return .@"border-block-end-style"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-inline-start-style")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"border-inline-start-style"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-inline-end-style")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"border-inline-end-style"; } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-top-width")) { const allowed_prefixes = VendorPrefix{ .none = true }; if (allowed_prefixes.contains(pre)) return .@"border-top-width"; @@ -690,18 +7606,492 @@ pub const PropertyId = union(PropertyIdTag) { } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-right-width")) { const allowed_prefixes = VendorPrefix{ .none = true }; if (allowed_prefixes.contains(pre)) return .@"border-right-width"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-block-start-width")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"border-block-start-width"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-block-end-width")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"border-block-end-width"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-inline-start-width")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"border-inline-start-width"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-inline-end-width")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"border-inline-end-width"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-top-left-radius")) { + const allowed_prefixes = VendorPrefix{ .webkit = true, .moz = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"border-top-left-radius" = pre }; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-top-right-radius")) { + const allowed_prefixes = VendorPrefix{ .webkit = true, .moz = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"border-top-right-radius" = pre }; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-bottom-left-radius")) { + const allowed_prefixes = VendorPrefix{ .webkit = true, .moz = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"border-bottom-left-radius" = pre }; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-bottom-right-radius")) { + const allowed_prefixes = VendorPrefix{ .webkit = true, .moz = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"border-bottom-right-radius" = pre }; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-start-start-radius")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"border-start-start-radius"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-start-end-radius")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"border-start-end-radius"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-end-start-radius")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"border-end-start-radius"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-end-end-radius")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"border-end-end-radius"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-radius")) { + const allowed_prefixes = VendorPrefix{ .webkit = true, .moz = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"border-radius" = pre }; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-image-source")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"border-image-source"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-image-outset")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"border-image-outset"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-image-repeat")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"border-image-repeat"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-image-width")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"border-image-width"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-image-slice")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"border-image-slice"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-image")) { + const allowed_prefixes = VendorPrefix{ .webkit = true, .moz = true, .o = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"border-image" = pre }; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-color")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"border-color"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-style")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"border-style"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-width")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"border-width"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-block-color")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"border-block-color"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-block-style")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"border-block-style"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-block-width")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"border-block-width"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-inline-color")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"border-inline-color"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-inline-style")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"border-inline-style"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-inline-width")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"border-inline-width"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .border; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-top")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"border-top"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-bottom")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"border-bottom"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-left")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"border-left"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-right")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"border-right"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-block")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"border-block"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-block-start")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"border-block-start"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-block-end")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"border-block-end"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-inline")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"border-inline"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-inline-start")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"border-inline-start"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-inline-end")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"border-inline-end"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "outline")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .outline; } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "outline-color")) { const allowed_prefixes = VendorPrefix{ .none = true }; if (allowed_prefixes.contains(pre)) return .@"outline-color"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "outline-style")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"outline-style"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "outline-width")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"outline-width"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "flex-direction")) { + const allowed_prefixes = VendorPrefix{ .webkit = true, .ms = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"flex-direction" = pre }; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "flex-wrap")) { + const allowed_prefixes = VendorPrefix{ .webkit = true, .ms = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"flex-wrap" = pre }; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "flex-flow")) { + const allowed_prefixes = VendorPrefix{ .webkit = true, .ms = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"flex-flow" = pre }; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "flex-grow")) { + const allowed_prefixes = VendorPrefix{ .webkit = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"flex-grow" = pre }; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "flex-shrink")) { + const allowed_prefixes = VendorPrefix{ .webkit = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"flex-shrink" = pre }; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "flex-basis")) { + const allowed_prefixes = VendorPrefix{ .webkit = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"flex-basis" = pre }; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "flex")) { + const allowed_prefixes = VendorPrefix{ .webkit = true, .ms = true }; + if (allowed_prefixes.contains(pre)) return .{ .flex = pre }; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "order")) { + const allowed_prefixes = VendorPrefix{ .webkit = true }; + if (allowed_prefixes.contains(pre)) return .{ .order = pre }; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "align-content")) { + const allowed_prefixes = VendorPrefix{ .webkit = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"align-content" = pre }; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "justify-content")) { + const allowed_prefixes = VendorPrefix{ .webkit = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"justify-content" = pre }; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "place-content")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"place-content"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "align-self")) { + const allowed_prefixes = VendorPrefix{ .webkit = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"align-self" = pre }; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "justify-self")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"justify-self"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "place-self")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"place-self"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "align-items")) { + const allowed_prefixes = VendorPrefix{ .webkit = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"align-items" = pre }; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "justify-items")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"justify-items"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "place-items")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"place-items"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "row-gap")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"row-gap"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "column-gap")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"column-gap"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "gap")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .gap; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "box-orient")) { + const allowed_prefixes = VendorPrefix{ .webkit = true, .moz = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"box-orient" = pre }; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "box-direction")) { + const allowed_prefixes = VendorPrefix{ .webkit = true, .moz = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"box-direction" = pre }; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "box-ordinal-group")) { + const allowed_prefixes = VendorPrefix{ .webkit = true, .moz = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"box-ordinal-group" = pre }; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "box-align")) { + const allowed_prefixes = VendorPrefix{ .webkit = true, .moz = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"box-align" = pre }; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "box-flex")) { + const allowed_prefixes = VendorPrefix{ .webkit = true, .moz = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"box-flex" = pre }; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "box-flex-group")) { + const allowed_prefixes = VendorPrefix{ .webkit = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"box-flex-group" = pre }; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "box-pack")) { + const allowed_prefixes = VendorPrefix{ .webkit = true, .moz = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"box-pack" = pre }; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "box-lines")) { + const allowed_prefixes = VendorPrefix{ .webkit = true, .moz = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"box-lines" = pre }; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "flex-pack")) { + const allowed_prefixes = VendorPrefix{ .ms = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"flex-pack" = pre }; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "flex-order")) { + const allowed_prefixes = VendorPrefix{ .ms = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"flex-order" = pre }; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "flex-align")) { + const allowed_prefixes = VendorPrefix{ .ms = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"flex-align" = pre }; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "flex-item-align")) { + const allowed_prefixes = VendorPrefix{ .ms = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"flex-item-align" = pre }; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "flex-line-pack")) { + const allowed_prefixes = VendorPrefix{ .ms = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"flex-line-pack" = pre }; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "flex-positive")) { + const allowed_prefixes = VendorPrefix{ .ms = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"flex-positive" = pre }; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "flex-negative")) { + const allowed_prefixes = VendorPrefix{ .ms = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"flex-negative" = pre }; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "flex-preferred-size")) { + const allowed_prefixes = VendorPrefix{ .ms = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"flex-preferred-size" = pre }; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "margin-top")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"margin-top"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "margin-bottom")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"margin-bottom"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "margin-left")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"margin-left"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "margin-right")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"margin-right"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "margin-block-start")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"margin-block-start"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "margin-block-end")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"margin-block-end"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "margin-inline-start")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"margin-inline-start"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "margin-inline-end")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"margin-inline-end"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "margin-block")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"margin-block"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "margin-inline")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"margin-inline"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "margin")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .margin; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "padding-top")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"padding-top"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "padding-bottom")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"padding-bottom"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "padding-left")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"padding-left"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "padding-right")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"padding-right"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "padding-block-start")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"padding-block-start"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "padding-block-end")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"padding-block-end"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "padding-inline-start")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"padding-inline-start"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "padding-inline-end")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"padding-inline-end"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "padding-block")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"padding-block"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "padding-inline")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"padding-inline"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "padding")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .padding; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "scroll-margin-top")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"scroll-margin-top"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "scroll-margin-bottom")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"scroll-margin-bottom"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "scroll-margin-left")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"scroll-margin-left"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "scroll-margin-right")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"scroll-margin-right"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "scroll-margin-block-start")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"scroll-margin-block-start"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "scroll-margin-block-end")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"scroll-margin-block-end"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "scroll-margin-inline-start")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"scroll-margin-inline-start"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "scroll-margin-inline-end")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"scroll-margin-inline-end"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "scroll-margin-block")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"scroll-margin-block"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "scroll-margin-inline")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"scroll-margin-inline"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "scroll-margin")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"scroll-margin"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "scroll-padding-top")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"scroll-padding-top"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "scroll-padding-bottom")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"scroll-padding-bottom"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "scroll-padding-left")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"scroll-padding-left"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "scroll-padding-right")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"scroll-padding-right"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "scroll-padding-block-start")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"scroll-padding-block-start"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "scroll-padding-block-end")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"scroll-padding-block-end"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "scroll-padding-inline-start")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"scroll-padding-inline-start"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "scroll-padding-inline-end")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"scroll-padding-inline-end"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "scroll-padding-block")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"scroll-padding-block"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "scroll-padding-inline")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"scroll-padding-inline"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "scroll-padding")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"scroll-padding"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "font-weight")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"font-weight"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "font-size")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"font-size"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "font-stretch")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"font-stretch"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "font-family")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"font-family"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "font-style")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"font-style"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "font-variant-caps")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"font-variant-caps"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "line-height")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"line-height"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "font")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .font; } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "text-decoration-color")) { const allowed_prefixes = VendorPrefix{ .webkit = true, .moz = true }; if (allowed_prefixes.contains(pre)) return .{ .@"text-decoration-color" = pre }; } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "text-emphasis-color")) { const allowed_prefixes = VendorPrefix{ .webkit = true }; if (allowed_prefixes.contains(pre)) return .{ .@"text-emphasis-color" = pre }; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "direction")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .direction; } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "composes")) { const allowed_prefixes = VendorPrefix{ .none = true }; if (allowed_prefixes.contains(pre)) return .composes; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "mask-image")) { + const allowed_prefixes = VendorPrefix{ .webkit = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"mask-image" = pre }; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "mask-mode")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"mask-mode"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "mask-repeat")) { + const allowed_prefixes = VendorPrefix{ .webkit = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"mask-repeat" = pre }; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "mask-position-x")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"mask-position-x"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "mask-position-y")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"mask-position-y"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "mask-position")) { + const allowed_prefixes = VendorPrefix{ .webkit = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"mask-position" = pre }; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "mask-clip")) { + const allowed_prefixes = VendorPrefix{ .webkit = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"mask-clip" = pre }; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "mask-origin")) { + const allowed_prefixes = VendorPrefix{ .webkit = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"mask-origin" = pre }; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "mask-size")) { + const allowed_prefixes = VendorPrefix{ .webkit = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"mask-size" = pre }; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "mask-composite")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"mask-composite"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "mask-type")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"mask-type"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "mask")) { + const allowed_prefixes = VendorPrefix{ .webkit = true }; + if (allowed_prefixes.contains(pre)) return .{ .mask = pre }; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "mask-border-source")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"mask-border-source"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "mask-border-mode")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"mask-border-mode"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "mask-border-slice")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"mask-border-slice"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "mask-border-width")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"mask-border-width"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "mask-border-outset")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"mask-border-outset"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "mask-border-repeat")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"mask-border-repeat"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "mask-border")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"mask-border"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "-webkit-mask-composite")) { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"-webkit-mask-composite"; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "mask-source-type")) { + const allowed_prefixes = VendorPrefix{ .webkit = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"mask-source-type" = pre }; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "mask-box-image")) { + const allowed_prefixes = VendorPrefix{ .webkit = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"mask-box-image" = pre }; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "mask-box-image-source")) { + const allowed_prefixes = VendorPrefix{ .webkit = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"mask-box-image-source" = pre }; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "mask-box-image-slice")) { + const allowed_prefixes = VendorPrefix{ .webkit = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"mask-box-image-slice" = pre }; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "mask-box-image-width")) { + const allowed_prefixes = VendorPrefix{ .webkit = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"mask-box-image-width" = pre }; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "mask-box-image-outset")) { + const allowed_prefixes = VendorPrefix{ .webkit = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"mask-box-image-outset" = pre }; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "mask-box-image-repeat")) { + const allowed_prefixes = VendorPrefix{ .webkit = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"mask-box-image-repeat" = pre }; } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "all")) {} else { return null; } @@ -712,7 +8102,51 @@ pub const PropertyId = union(PropertyIdTag) { pub fn withPrefix(this: *const PropertyId, pre: VendorPrefix) PropertyId { return switch (this.*) { .@"background-color" => .@"background-color", + .@"background-image" => .@"background-image", + .@"background-position-x" => .@"background-position-x", + .@"background-position-y" => .@"background-position-y", + .@"background-position" => .@"background-position", + .@"background-size" => .@"background-size", + .@"background-repeat" => .@"background-repeat", + .@"background-attachment" => .@"background-attachment", + .@"background-clip" => .{ .@"background-clip" = pre }, + .@"background-origin" => .@"background-origin", + .background => .background, + .@"box-shadow" => .{ .@"box-shadow" = pre }, + .opacity => .opacity, .color => .color, + .display => .display, + .visibility => .visibility, + .width => .width, + .height => .height, + .@"min-width" => .@"min-width", + .@"min-height" => .@"min-height", + .@"max-width" => .@"max-width", + .@"max-height" => .@"max-height", + .@"block-size" => .@"block-size", + .@"inline-size" => .@"inline-size", + .@"min-block-size" => .@"min-block-size", + .@"min-inline-size" => .@"min-inline-size", + .@"max-block-size" => .@"max-block-size", + .@"max-inline-size" => .@"max-inline-size", + .@"box-sizing" => .{ .@"box-sizing" = pre }, + .@"aspect-ratio" => .@"aspect-ratio", + .overflow => .overflow, + .@"overflow-x" => .@"overflow-x", + .@"overflow-y" => .@"overflow-y", + .@"text-overflow" => .{ .@"text-overflow" = pre }, + .position => .position, + .top => .top, + .bottom => .bottom, + .left => .left, + .right => .right, + .@"inset-block-start" => .@"inset-block-start", + .@"inset-block-end" => .@"inset-block-end", + .@"inset-inline-start" => .@"inset-inline-start", + .@"inset-inline-end" => .@"inset-inline-end", + .@"inset-block" => .@"inset-block", + .@"inset-inline" => .@"inset-inline", + .inset => .inset, .@"border-spacing" => .@"border-spacing", .@"border-top-color" => .@"border-top-color", .@"border-bottom-color" => .@"border-bottom-color", @@ -728,14 +8162,174 @@ pub const PropertyId = union(PropertyIdTag) { .@"border-right-style" => .@"border-right-style", .@"border-block-start-style" => .@"border-block-start-style", .@"border-block-end-style" => .@"border-block-end-style", + .@"border-inline-start-style" => .@"border-inline-start-style", + .@"border-inline-end-style" => .@"border-inline-end-style", .@"border-top-width" => .@"border-top-width", .@"border-bottom-width" => .@"border-bottom-width", .@"border-left-width" => .@"border-left-width", .@"border-right-width" => .@"border-right-width", + .@"border-block-start-width" => .@"border-block-start-width", + .@"border-block-end-width" => .@"border-block-end-width", + .@"border-inline-start-width" => .@"border-inline-start-width", + .@"border-inline-end-width" => .@"border-inline-end-width", + .@"border-top-left-radius" => .{ .@"border-top-left-radius" = pre }, + .@"border-top-right-radius" => .{ .@"border-top-right-radius" = pre }, + .@"border-bottom-left-radius" => .{ .@"border-bottom-left-radius" = pre }, + .@"border-bottom-right-radius" => .{ .@"border-bottom-right-radius" = pre }, + .@"border-start-start-radius" => .@"border-start-start-radius", + .@"border-start-end-radius" => .@"border-start-end-radius", + .@"border-end-start-radius" => .@"border-end-start-radius", + .@"border-end-end-radius" => .@"border-end-end-radius", + .@"border-radius" => .{ .@"border-radius" = pre }, + .@"border-image-source" => .@"border-image-source", + .@"border-image-outset" => .@"border-image-outset", + .@"border-image-repeat" => .@"border-image-repeat", + .@"border-image-width" => .@"border-image-width", + .@"border-image-slice" => .@"border-image-slice", + .@"border-image" => .{ .@"border-image" = pre }, + .@"border-color" => .@"border-color", + .@"border-style" => .@"border-style", + .@"border-width" => .@"border-width", + .@"border-block-color" => .@"border-block-color", + .@"border-block-style" => .@"border-block-style", + .@"border-block-width" => .@"border-block-width", + .@"border-inline-color" => .@"border-inline-color", + .@"border-inline-style" => .@"border-inline-style", + .@"border-inline-width" => .@"border-inline-width", + .border => .border, + .@"border-top" => .@"border-top", + .@"border-bottom" => .@"border-bottom", + .@"border-left" => .@"border-left", + .@"border-right" => .@"border-right", + .@"border-block" => .@"border-block", + .@"border-block-start" => .@"border-block-start", + .@"border-block-end" => .@"border-block-end", + .@"border-inline" => .@"border-inline", + .@"border-inline-start" => .@"border-inline-start", + .@"border-inline-end" => .@"border-inline-end", + .outline => .outline, .@"outline-color" => .@"outline-color", + .@"outline-style" => .@"outline-style", + .@"outline-width" => .@"outline-width", + .@"flex-direction" => .{ .@"flex-direction" = pre }, + .@"flex-wrap" => .{ .@"flex-wrap" = pre }, + .@"flex-flow" => .{ .@"flex-flow" = pre }, + .@"flex-grow" => .{ .@"flex-grow" = pre }, + .@"flex-shrink" => .{ .@"flex-shrink" = pre }, + .@"flex-basis" => .{ .@"flex-basis" = pre }, + .flex => .{ .flex = pre }, + .order => .{ .order = pre }, + .@"align-content" => .{ .@"align-content" = pre }, + .@"justify-content" => .{ .@"justify-content" = pre }, + .@"place-content" => .@"place-content", + .@"align-self" => .{ .@"align-self" = pre }, + .@"justify-self" => .@"justify-self", + .@"place-self" => .@"place-self", + .@"align-items" => .{ .@"align-items" = pre }, + .@"justify-items" => .@"justify-items", + .@"place-items" => .@"place-items", + .@"row-gap" => .@"row-gap", + .@"column-gap" => .@"column-gap", + .gap => .gap, + .@"box-orient" => .{ .@"box-orient" = pre }, + .@"box-direction" => .{ .@"box-direction" = pre }, + .@"box-ordinal-group" => .{ .@"box-ordinal-group" = pre }, + .@"box-align" => .{ .@"box-align" = pre }, + .@"box-flex" => .{ .@"box-flex" = pre }, + .@"box-flex-group" => .{ .@"box-flex-group" = pre }, + .@"box-pack" => .{ .@"box-pack" = pre }, + .@"box-lines" => .{ .@"box-lines" = pre }, + .@"flex-pack" => .{ .@"flex-pack" = pre }, + .@"flex-order" => .{ .@"flex-order" = pre }, + .@"flex-align" => .{ .@"flex-align" = pre }, + .@"flex-item-align" => .{ .@"flex-item-align" = pre }, + .@"flex-line-pack" => .{ .@"flex-line-pack" = pre }, + .@"flex-positive" => .{ .@"flex-positive" = pre }, + .@"flex-negative" => .{ .@"flex-negative" = pre }, + .@"flex-preferred-size" => .{ .@"flex-preferred-size" = pre }, + .@"margin-top" => .@"margin-top", + .@"margin-bottom" => .@"margin-bottom", + .@"margin-left" => .@"margin-left", + .@"margin-right" => .@"margin-right", + .@"margin-block-start" => .@"margin-block-start", + .@"margin-block-end" => .@"margin-block-end", + .@"margin-inline-start" => .@"margin-inline-start", + .@"margin-inline-end" => .@"margin-inline-end", + .@"margin-block" => .@"margin-block", + .@"margin-inline" => .@"margin-inline", + .margin => .margin, + .@"padding-top" => .@"padding-top", + .@"padding-bottom" => .@"padding-bottom", + .@"padding-left" => .@"padding-left", + .@"padding-right" => .@"padding-right", + .@"padding-block-start" => .@"padding-block-start", + .@"padding-block-end" => .@"padding-block-end", + .@"padding-inline-start" => .@"padding-inline-start", + .@"padding-inline-end" => .@"padding-inline-end", + .@"padding-block" => .@"padding-block", + .@"padding-inline" => .@"padding-inline", + .padding => .padding, + .@"scroll-margin-top" => .@"scroll-margin-top", + .@"scroll-margin-bottom" => .@"scroll-margin-bottom", + .@"scroll-margin-left" => .@"scroll-margin-left", + .@"scroll-margin-right" => .@"scroll-margin-right", + .@"scroll-margin-block-start" => .@"scroll-margin-block-start", + .@"scroll-margin-block-end" => .@"scroll-margin-block-end", + .@"scroll-margin-inline-start" => .@"scroll-margin-inline-start", + .@"scroll-margin-inline-end" => .@"scroll-margin-inline-end", + .@"scroll-margin-block" => .@"scroll-margin-block", + .@"scroll-margin-inline" => .@"scroll-margin-inline", + .@"scroll-margin" => .@"scroll-margin", + .@"scroll-padding-top" => .@"scroll-padding-top", + .@"scroll-padding-bottom" => .@"scroll-padding-bottom", + .@"scroll-padding-left" => .@"scroll-padding-left", + .@"scroll-padding-right" => .@"scroll-padding-right", + .@"scroll-padding-block-start" => .@"scroll-padding-block-start", + .@"scroll-padding-block-end" => .@"scroll-padding-block-end", + .@"scroll-padding-inline-start" => .@"scroll-padding-inline-start", + .@"scroll-padding-inline-end" => .@"scroll-padding-inline-end", + .@"scroll-padding-block" => .@"scroll-padding-block", + .@"scroll-padding-inline" => .@"scroll-padding-inline", + .@"scroll-padding" => .@"scroll-padding", + .@"font-weight" => .@"font-weight", + .@"font-size" => .@"font-size", + .@"font-stretch" => .@"font-stretch", + .@"font-family" => .@"font-family", + .@"font-style" => .@"font-style", + .@"font-variant-caps" => .@"font-variant-caps", + .@"line-height" => .@"line-height", + .font => .font, .@"text-decoration-color" => .{ .@"text-decoration-color" = pre }, .@"text-emphasis-color" => .{ .@"text-emphasis-color" = pre }, + .direction => .direction, .composes => .composes, + .@"mask-image" => .{ .@"mask-image" = pre }, + .@"mask-mode" => .@"mask-mode", + .@"mask-repeat" => .{ .@"mask-repeat" = pre }, + .@"mask-position-x" => .@"mask-position-x", + .@"mask-position-y" => .@"mask-position-y", + .@"mask-position" => .{ .@"mask-position" = pre }, + .@"mask-clip" => .{ .@"mask-clip" = pre }, + .@"mask-origin" => .{ .@"mask-origin" = pre }, + .@"mask-size" => .{ .@"mask-size" = pre }, + .@"mask-composite" => .@"mask-composite", + .@"mask-type" => .@"mask-type", + .mask => .{ .mask = pre }, + .@"mask-border-source" => .@"mask-border-source", + .@"mask-border-mode" => .@"mask-border-mode", + .@"mask-border-slice" => .@"mask-border-slice", + .@"mask-border-width" => .@"mask-border-width", + .@"mask-border-outset" => .@"mask-border-outset", + .@"mask-border-repeat" => .@"mask-border-repeat", + .@"mask-border" => .@"mask-border", + .@"-webkit-mask-composite" => .@"-webkit-mask-composite", + .@"mask-source-type" => .{ .@"mask-source-type" = pre }, + .@"mask-box-image" => .{ .@"mask-box-image" = pre }, + .@"mask-box-image-source" => .{ .@"mask-box-image-source" = pre }, + .@"mask-box-image-slice" => .{ .@"mask-box-image-slice" = pre }, + .@"mask-box-image-width" => .{ .@"mask-box-image-width" = pre }, + .@"mask-box-image-outset" => .{ .@"mask-box-image-outset" = pre }, + .@"mask-box-image-repeat" => .{ .@"mask-box-image-repeat" = pre }, else => this.*, }; } @@ -743,7 +8337,59 @@ pub const PropertyId = union(PropertyIdTag) { pub fn addPrefix(this: *PropertyId, pre: VendorPrefix) void { return switch (this.*) { .@"background-color" => {}, + .@"background-image" => {}, + .@"background-position-x" => {}, + .@"background-position-y" => {}, + .@"background-position" => {}, + .@"background-size" => {}, + .@"background-repeat" => {}, + .@"background-attachment" => {}, + .@"background-clip" => |*p| { + p.insert(pre); + }, + .@"background-origin" => {}, + .background => {}, + .@"box-shadow" => |*p| { + p.insert(pre); + }, + .opacity => {}, .color => {}, + .display => {}, + .visibility => {}, + .width => {}, + .height => {}, + .@"min-width" => {}, + .@"min-height" => {}, + .@"max-width" => {}, + .@"max-height" => {}, + .@"block-size" => {}, + .@"inline-size" => {}, + .@"min-block-size" => {}, + .@"min-inline-size" => {}, + .@"max-block-size" => {}, + .@"max-inline-size" => {}, + .@"box-sizing" => |*p| { + p.insert(pre); + }, + .@"aspect-ratio" => {}, + .overflow => {}, + .@"overflow-x" => {}, + .@"overflow-y" => {}, + .@"text-overflow" => |*p| { + p.insert(pre); + }, + .position => {}, + .top => {}, + .bottom => {}, + .left => {}, + .right => {}, + .@"inset-block-start" => {}, + .@"inset-block-end" => {}, + .@"inset-inline-start" => {}, + .@"inset-inline-end" => {}, + .@"inset-block" => {}, + .@"inset-inline" => {}, + .inset => {}, .@"border-spacing" => {}, .@"border-top-color" => {}, .@"border-bottom-color" => {}, @@ -759,25 +8405,348 @@ pub const PropertyId = union(PropertyIdTag) { .@"border-right-style" => {}, .@"border-block-start-style" => {}, .@"border-block-end-style" => {}, + .@"border-inline-start-style" => {}, + .@"border-inline-end-style" => {}, .@"border-top-width" => {}, .@"border-bottom-width" => {}, .@"border-left-width" => {}, .@"border-right-width" => {}, + .@"border-block-start-width" => {}, + .@"border-block-end-width" => {}, + .@"border-inline-start-width" => {}, + .@"border-inline-end-width" => {}, + .@"border-top-left-radius" => |*p| { + p.insert(pre); + }, + .@"border-top-right-radius" => |*p| { + p.insert(pre); + }, + .@"border-bottom-left-radius" => |*p| { + p.insert(pre); + }, + .@"border-bottom-right-radius" => |*p| { + p.insert(pre); + }, + .@"border-start-start-radius" => {}, + .@"border-start-end-radius" => {}, + .@"border-end-start-radius" => {}, + .@"border-end-end-radius" => {}, + .@"border-radius" => |*p| { + p.insert(pre); + }, + .@"border-image-source" => {}, + .@"border-image-outset" => {}, + .@"border-image-repeat" => {}, + .@"border-image-width" => {}, + .@"border-image-slice" => {}, + .@"border-image" => |*p| { + p.insert(pre); + }, + .@"border-color" => {}, + .@"border-style" => {}, + .@"border-width" => {}, + .@"border-block-color" => {}, + .@"border-block-style" => {}, + .@"border-block-width" => {}, + .@"border-inline-color" => {}, + .@"border-inline-style" => {}, + .@"border-inline-width" => {}, + .border => {}, + .@"border-top" => {}, + .@"border-bottom" => {}, + .@"border-left" => {}, + .@"border-right" => {}, + .@"border-block" => {}, + .@"border-block-start" => {}, + .@"border-block-end" => {}, + .@"border-inline" => {}, + .@"border-inline-start" => {}, + .@"border-inline-end" => {}, + .outline => {}, .@"outline-color" => {}, + .@"outline-style" => {}, + .@"outline-width" => {}, + .@"flex-direction" => |*p| { + p.insert(pre); + }, + .@"flex-wrap" => |*p| { + p.insert(pre); + }, + .@"flex-flow" => |*p| { + p.insert(pre); + }, + .@"flex-grow" => |*p| { + p.insert(pre); + }, + .@"flex-shrink" => |*p| { + p.insert(pre); + }, + .@"flex-basis" => |*p| { + p.insert(pre); + }, + .flex => |*p| { + p.insert(pre); + }, + .order => |*p| { + p.insert(pre); + }, + .@"align-content" => |*p| { + p.insert(pre); + }, + .@"justify-content" => |*p| { + p.insert(pre); + }, + .@"place-content" => {}, + .@"align-self" => |*p| { + p.insert(pre); + }, + .@"justify-self" => {}, + .@"place-self" => {}, + .@"align-items" => |*p| { + p.insert(pre); + }, + .@"justify-items" => {}, + .@"place-items" => {}, + .@"row-gap" => {}, + .@"column-gap" => {}, + .gap => {}, + .@"box-orient" => |*p| { + p.insert(pre); + }, + .@"box-direction" => |*p| { + p.insert(pre); + }, + .@"box-ordinal-group" => |*p| { + p.insert(pre); + }, + .@"box-align" => |*p| { + p.insert(pre); + }, + .@"box-flex" => |*p| { + p.insert(pre); + }, + .@"box-flex-group" => |*p| { + p.insert(pre); + }, + .@"box-pack" => |*p| { + p.insert(pre); + }, + .@"box-lines" => |*p| { + p.insert(pre); + }, + .@"flex-pack" => |*p| { + p.insert(pre); + }, + .@"flex-order" => |*p| { + p.insert(pre); + }, + .@"flex-align" => |*p| { + p.insert(pre); + }, + .@"flex-item-align" => |*p| { + p.insert(pre); + }, + .@"flex-line-pack" => |*p| { + p.insert(pre); + }, + .@"flex-positive" => |*p| { + p.insert(pre); + }, + .@"flex-negative" => |*p| { + p.insert(pre); + }, + .@"flex-preferred-size" => |*p| { + p.insert(pre); + }, + .@"margin-top" => {}, + .@"margin-bottom" => {}, + .@"margin-left" => {}, + .@"margin-right" => {}, + .@"margin-block-start" => {}, + .@"margin-block-end" => {}, + .@"margin-inline-start" => {}, + .@"margin-inline-end" => {}, + .@"margin-block" => {}, + .@"margin-inline" => {}, + .margin => {}, + .@"padding-top" => {}, + .@"padding-bottom" => {}, + .@"padding-left" => {}, + .@"padding-right" => {}, + .@"padding-block-start" => {}, + .@"padding-block-end" => {}, + .@"padding-inline-start" => {}, + .@"padding-inline-end" => {}, + .@"padding-block" => {}, + .@"padding-inline" => {}, + .padding => {}, + .@"scroll-margin-top" => {}, + .@"scroll-margin-bottom" => {}, + .@"scroll-margin-left" => {}, + .@"scroll-margin-right" => {}, + .@"scroll-margin-block-start" => {}, + .@"scroll-margin-block-end" => {}, + .@"scroll-margin-inline-start" => {}, + .@"scroll-margin-inline-end" => {}, + .@"scroll-margin-block" => {}, + .@"scroll-margin-inline" => {}, + .@"scroll-margin" => {}, + .@"scroll-padding-top" => {}, + .@"scroll-padding-bottom" => {}, + .@"scroll-padding-left" => {}, + .@"scroll-padding-right" => {}, + .@"scroll-padding-block-start" => {}, + .@"scroll-padding-block-end" => {}, + .@"scroll-padding-inline-start" => {}, + .@"scroll-padding-inline-end" => {}, + .@"scroll-padding-block" => {}, + .@"scroll-padding-inline" => {}, + .@"scroll-padding" => {}, + .@"font-weight" => {}, + .@"font-size" => {}, + .@"font-stretch" => {}, + .@"font-family" => {}, + .@"font-style" => {}, + .@"font-variant-caps" => {}, + .@"line-height" => {}, + .font => {}, .@"text-decoration-color" => |*p| { p.insert(pre); }, .@"text-emphasis-color" => |*p| { p.insert(pre); }, + .direction => {}, .composes => {}, + .@"mask-image" => |*p| { + p.insert(pre); + }, + .@"mask-mode" => {}, + .@"mask-repeat" => |*p| { + p.insert(pre); + }, + .@"mask-position-x" => {}, + .@"mask-position-y" => {}, + .@"mask-position" => |*p| { + p.insert(pre); + }, + .@"mask-clip" => |*p| { + p.insert(pre); + }, + .@"mask-origin" => |*p| { + p.insert(pre); + }, + .@"mask-size" => |*p| { + p.insert(pre); + }, + .@"mask-composite" => {}, + .@"mask-type" => {}, + .mask => |*p| { + p.insert(pre); + }, + .@"mask-border-source" => {}, + .@"mask-border-mode" => {}, + .@"mask-border-slice" => {}, + .@"mask-border-width" => {}, + .@"mask-border-outset" => {}, + .@"mask-border-repeat" => {}, + .@"mask-border" => {}, + .@"-webkit-mask-composite" => {}, + .@"mask-source-type" => |*p| { + p.insert(pre); + }, + .@"mask-box-image" => |*p| { + p.insert(pre); + }, + .@"mask-box-image-source" => |*p| { + p.insert(pre); + }, + .@"mask-box-image-slice" => |*p| { + p.insert(pre); + }, + .@"mask-box-image-width" => |*p| { + p.insert(pre); + }, + .@"mask-box-image-outset" => |*p| { + p.insert(pre); + }, + .@"mask-box-image-repeat" => |*p| { + p.insert(pre); + }, else => {}, }; } + + pub inline fn deepClone(this: *const PropertyId, _: std.mem.Allocator) PropertyId { + return this.*; + } + + pub fn eql(lhs: *const PropertyId, rhs: *const PropertyId) bool { + if (@intFromEnum(lhs.*) != @intFromEnum(rhs.*)) return false; + inline for (bun.meta.EnumFields(PropertyId), std.meta.fields(PropertyId)) |enum_field, union_field| { + if (enum_field.value == @intFromEnum(lhs.*)) { + if (comptime union_field.type == css.VendorPrefix) { + return @field(lhs, union_field.name).eql(@field(rhs, union_field.name)); + } else { + return true; + } + } + } + unreachable; + } + + pub fn hash(this: *const PropertyId, hasher: *std.hash.Wyhash) void { + const tag = @intFromEnum(this.*); + hasher.update(std.mem.asBytes(&tag)); + } }; pub const PropertyIdTag = enum(u16) { @"background-color", + @"background-image", + @"background-position-x", + @"background-position-y", + @"background-position", + @"background-size", + @"background-repeat", + @"background-attachment", + @"background-clip", + @"background-origin", + background, + @"box-shadow", + opacity, color, + display, + visibility, + width, + height, + @"min-width", + @"min-height", + @"max-width", + @"max-height", + @"block-size", + @"inline-size", + @"min-block-size", + @"min-inline-size", + @"max-block-size", + @"max-inline-size", + @"box-sizing", + @"aspect-ratio", + overflow, + @"overflow-x", + @"overflow-y", + @"text-overflow", + position, + top, + bottom, + left, + right, + @"inset-block-start", + @"inset-block-end", + @"inset-inline-start", + @"inset-inline-end", + @"inset-block", + @"inset-inline", + inset, @"border-spacing", @"border-top-color", @"border-bottom-color", @@ -793,14 +8762,174 @@ pub const PropertyIdTag = enum(u16) { @"border-right-style", @"border-block-start-style", @"border-block-end-style", + @"border-inline-start-style", + @"border-inline-end-style", @"border-top-width", @"border-bottom-width", @"border-left-width", @"border-right-width", + @"border-block-start-width", + @"border-block-end-width", + @"border-inline-start-width", + @"border-inline-end-width", + @"border-top-left-radius", + @"border-top-right-radius", + @"border-bottom-left-radius", + @"border-bottom-right-radius", + @"border-start-start-radius", + @"border-start-end-radius", + @"border-end-start-radius", + @"border-end-end-radius", + @"border-radius", + @"border-image-source", + @"border-image-outset", + @"border-image-repeat", + @"border-image-width", + @"border-image-slice", + @"border-image", + @"border-color", + @"border-style", + @"border-width", + @"border-block-color", + @"border-block-style", + @"border-block-width", + @"border-inline-color", + @"border-inline-style", + @"border-inline-width", + border, + @"border-top", + @"border-bottom", + @"border-left", + @"border-right", + @"border-block", + @"border-block-start", + @"border-block-end", + @"border-inline", + @"border-inline-start", + @"border-inline-end", + outline, @"outline-color", + @"outline-style", + @"outline-width", + @"flex-direction", + @"flex-wrap", + @"flex-flow", + @"flex-grow", + @"flex-shrink", + @"flex-basis", + flex, + order, + @"align-content", + @"justify-content", + @"place-content", + @"align-self", + @"justify-self", + @"place-self", + @"align-items", + @"justify-items", + @"place-items", + @"row-gap", + @"column-gap", + gap, + @"box-orient", + @"box-direction", + @"box-ordinal-group", + @"box-align", + @"box-flex", + @"box-flex-group", + @"box-pack", + @"box-lines", + @"flex-pack", + @"flex-order", + @"flex-align", + @"flex-item-align", + @"flex-line-pack", + @"flex-positive", + @"flex-negative", + @"flex-preferred-size", + @"margin-top", + @"margin-bottom", + @"margin-left", + @"margin-right", + @"margin-block-start", + @"margin-block-end", + @"margin-inline-start", + @"margin-inline-end", + @"margin-block", + @"margin-inline", + margin, + @"padding-top", + @"padding-bottom", + @"padding-left", + @"padding-right", + @"padding-block-start", + @"padding-block-end", + @"padding-inline-start", + @"padding-inline-end", + @"padding-block", + @"padding-inline", + padding, + @"scroll-margin-top", + @"scroll-margin-bottom", + @"scroll-margin-left", + @"scroll-margin-right", + @"scroll-margin-block-start", + @"scroll-margin-block-end", + @"scroll-margin-inline-start", + @"scroll-margin-inline-end", + @"scroll-margin-block", + @"scroll-margin-inline", + @"scroll-margin", + @"scroll-padding-top", + @"scroll-padding-bottom", + @"scroll-padding-left", + @"scroll-padding-right", + @"scroll-padding-block-start", + @"scroll-padding-block-end", + @"scroll-padding-inline-start", + @"scroll-padding-inline-end", + @"scroll-padding-block", + @"scroll-padding-inline", + @"scroll-padding", + @"font-weight", + @"font-size", + @"font-stretch", + @"font-family", + @"font-style", + @"font-variant-caps", + @"line-height", + font, @"text-decoration-color", @"text-emphasis-color", + direction, composes, + @"mask-image", + @"mask-mode", + @"mask-repeat", + @"mask-position-x", + @"mask-position-y", + @"mask-position", + @"mask-clip", + @"mask-origin", + @"mask-size", + @"mask-composite", + @"mask-type", + mask, + @"mask-border-source", + @"mask-border-mode", + @"mask-border-slice", + @"mask-border-width", + @"mask-border-outset", + @"mask-border-repeat", + @"mask-border", + @"-webkit-mask-composite", + @"mask-source-type", + @"mask-box-image", + @"mask-box-image-source", + @"mask-box-image-slice", + @"mask-box-image-width", + @"mask-box-image-outset", + @"mask-box-image-repeat", all, unparsed, custom, diff --git a/src/css/properties/size.zig b/src/css/properties/size.zig index 9c3e535412..a2d34080dd 100644 --- a/src/css/properties/size.zig +++ b/src/css/properties/size.zig @@ -57,6 +57,109 @@ pub const Size = union(enum) { stretch: css.VendorPrefix, /// The `contain` keyword. contain, + + pub fn parse(input: *css.Parser) css.Result(Size) { + const Enum = enum { + auto, + min_content, + @"-webkit-min-content", + @"-moz-min-content", + max_content, + @"-webkit-max-content", + @"-moz-max-content", + stretch, + @"-webkit-fill-available", + @"-moz-available", + fit_content, + @"-webkit-fit-content", + @"-moz-fit-content", + contain, + }; + const Map = comptime bun.ComptimeEnumMap(Enum); + const res = input.tryParse(struct { + pub fn parseFn(i: *css.Parser) css.Result(Size) { + const ident = switch (i.expectIdent()) { + .result => |v| v, + .err => |e| return .{ .err = e }, + }; + + if (Map.get(ident)) |res| { + return .{ .result = switch (res) { + .auto => .auto, + .min_content => .{ .min_content = css.VendorPrefix{ .none = true } }, + .@"-webkit-min-content" => .{ .min_content = css.VendorPrefix{ .webkit = true } }, + .@"-moz-min-content" => .{ .min_content = css.VendorPrefix{ .moz = true } }, + .max_content => .{ .max_content = css.VendorPrefix{ .none = true } }, + .@"-webkit-max-content" => .{ .max_content = css.VendorPrefix{ .webkit = true } }, + .@"-moz-max-content" => .{ .max_content = css.VendorPrefix{ .moz = true } }, + .stretch => .{ .stretch = css.VendorPrefix{ .none = true } }, + .@"-webkit-fill-available" => .{ .stretch = css.VendorPrefix{ .webkit = true } }, + .@"-moz-available" => .{ .stretch = css.VendorPrefix{ .moz = true } }, + .fit_content => .{ .fit_content = css.VendorPrefix{ .none = true } }, + .@"-webkit-fit-content" => .{ .fit_content = css.VendorPrefix{ .webkit = true } }, + .@"-moz-fit-content" => .{ .fit_content = css.VendorPrefix{ .moz = true } }, + .contain => .contain, + } }; + } else return .{ .err = i.newCustomError(css.ParserError.invalid_value) }; + } + }.parseFn, .{}); + + if (res == .result) return res; + + if (input.tryParse(parseFitContent, .{}).asValue()) |v| { + return .{ .result = Size{ .fit_content_function = v } }; + } + + const lp = switch (input.tryParse(LengthPercentage.parse, .{})) { + .result => |v| v, + .err => |e| return .{ .err = e }, + }; + return .{ .result = Size{ .length_percentage = lp } }; + } + + pub fn toCss(this: *const Size, comptime W: type, dest: *css.Printer(W)) css.PrintErr!void { + return switch (this.*) { + .auto => dest.writeStr("auto"), + .contain => dest.writeStr("contain"), + .min_content => |vp| { + try vp.toCss(W, dest); + try dest.writeStr("min-content"); + }, + .max_content => |vp| { + try vp.toCss(W, dest); + try dest.writeStr("max-content"); + }, + .fit_content => |vp| { + try vp.toCss(W, dest); + try dest.writeStr("fit-content"); + }, + .stretch => |vp| { + if (vp.eql(css.VendorPrefix{ .none = true })) { + try dest.writeStr("stretch"); + } else if (vp.eql(css.VendorPrefix{ .webkit = true })) { + try dest.writeStr("-webkit-fill-available"); + } else if (vp.eql(css.VendorPrefix{ .moz = true })) { + try dest.writeStr("-moz-available"); + } else { + bun.unreachablePanic("Unexpected vendor prefixes", .{}); + } + }, + .fit_content_function => |l| { + try dest.writeStr("fit-content("); + try l.toCss(W, dest); + try dest.writeChar(')'); + }, + .length_percentage => |l| return l.toCss(W, dest), + }; + } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } }; /// A value for the [minimum](https://drafts.csswg.org/css-sizing-3/#min-size-properties) @@ -79,6 +182,125 @@ pub const MaxSize = union(enum) { stretch: css.VendorPrefix, /// The `contain` keyword. contain, + + pub fn parse(input: *css.Parser) css.Result(MaxSize) { + const Ident = enum { + none, + min_content, + webkit_min_content, + moz_min_content, + max_content, + webkit_max_content, + moz_max_content, + stretch, + webkit_fill_available, + moz_available, + fit_content, + webkit_fit_content, + moz_fit_content, + contain, + }; + + const IdentMap = bun.ComptimeStringMap(Ident, .{ + .{ "none", .none }, + .{ "min-content", .min_content }, + .{ "-webkit-min-content", .webkit_min_content }, + .{ "-moz-min-content", .moz_min_content }, + .{ "max-content", .max_content }, + .{ "-webkit-max-content", .webkit_max_content }, + .{ "-moz-max-content", .moz_max_content }, + .{ "stretch", .stretch }, + .{ "-webkit-fill-available", .webkit_fill_available }, + .{ "-moz-available", .moz_available }, + .{ "fit-content", .fit_content }, + .{ "-webkit-fit-content", .webkit_fit_content }, + .{ "-moz-fit-content", .moz_fit_content }, + .{ "contain", .contain }, + }); + + const res = input.tryParse(struct { + fn parse(i: *css.Parser) css.Result(MaxSize) { + const ident = switch (i.expectIdent()) { + .result => |v| v, + .err => |e| return .{ .err = e }, + }; + const mapped = IdentMap.get(ident) orelse return .{ .err = i.newCustomError(css.ParserError.invalid_value) }; + return .{ .result = switch (mapped) { + .none => .none, + .min_content => .{ .min_content = .{ .none = true } }, + .webkit_min_content => .{ .min_content = .{ .webkit = true } }, + .moz_min_content => .{ .min_content = .{ .moz = true } }, + .max_content => .{ .max_content = .{ .none = true } }, + .webkit_max_content => .{ .max_content = .{ .webkit = true } }, + .moz_max_content => .{ .max_content = .{ .moz = true } }, + .stretch => .{ .stretch = .{ .none = true } }, + .webkit_fill_available => .{ .stretch = .{ .webkit = true } }, + .moz_available => .{ .stretch = .{ .moz = true } }, + .fit_content => .{ .fit_content = .{ .none = true } }, + .webkit_fit_content => .{ .fit_content = .{ .webkit = true } }, + .moz_fit_content => .{ .fit_content = .{ .moz = true } }, + .contain => .contain, + } }; + } + }.parse, .{}); + + if (res.isOk()) { + return res; + } + + if (parseFitContent(input).asValue()) |v| { + return .{ .result = .{ .fit_content_function = v } }; + } + + return switch (LengthPercentage.parse(input)) { + .result => |v| .{ .result = .{ .length_percentage = v } }, + .err => |e| .{ .err = e }, + }; + } + + pub fn toCss(this: *const MaxSize, comptime W: type, dest: *css.Printer(W)) css.PrintErr!void { + switch (this.*) { + .none => try dest.writeStr("none"), + .contain => try dest.writeStr("contain"), + .min_content => |vp| { + try vp.toCss(W, dest); + try dest.writeStr("min-content"); + }, + .max_content => |vp| { + try vp.toCss(W, dest); + try dest.writeStr("max-content"); + }, + .fit_content => |vp| { + try vp.toCss(W, dest); + try dest.writeStr("fit-content"); + }, + .stretch => |vp| { + if (css.VendorPrefix.eql(vp, css.VendorPrefix{ .none = true })) { + try dest.writeStr("stretch"); + } else if (css.VendorPrefix.eql(vp, css.VendorPrefix{ .webkit = true })) { + try dest.writeStr("-webkit-fill-available"); + } else if (css.VendorPrefix.eql(vp, css.VendorPrefix{ .moz = true })) { + try dest.writeStr("-moz-available"); + } else { + bun.unreachablePanic("Unexpected vendor prefixes", .{}); + } + }, + .fit_content_function => |l| { + try dest.writeStr("fit-content("); + try l.toCss(W, dest); + try dest.writeChar(')'); + }, + .length_percentage => |l| try l.toCss(W, dest), + } + } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } }; /// A value for the [aspect-ratio](https://drafts.csswg.org/css-sizing-4/#aspect-ratio) property. @@ -97,7 +319,7 @@ pub const AspectRatio = struct { auto = input.tryParse(css.Parser.expectIdentMatching, .{"auto"}); } if (auto.isErr() and ratio.isErr()) { - return .{ .err = location.newCustomError(css.ParserError.invalid_value) }; + return .{ .err = location.newCustomError(css.ParserError{ .invalid_value = {} }) }; } return .{ @@ -118,4 +340,17 @@ pub const AspectRatio = struct { try ratio.toCss(W, dest); } } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } }; + +fn parseFitContent(input: *css.Parser) css.Result(LengthPercentage) { + if (input.expectFunctionMatching("fit-content").asErr()) |e| return .{ .err = e }; + return input.parseNestedBlock(LengthPercentage, {}, css.voidWrap(LengthPercentage, LengthPercentage.parse)); +} diff --git a/src/css/properties/text.zig b/src/css/properties/text.zig index 03bdc3d3aa..1848d72833 100644 --- a/src/css/properties/text.zig +++ b/src/css/properties/text.zig @@ -183,7 +183,14 @@ pub const TextSizeAdjust = union(enum) { }; /// A value for the [direction](https://drafts.csswg.org/css-writing-modes-3/#direction) property. -pub const Direction = css.DefineEnumProperty(@compileError(css.todo_stuff.depth)); +pub const Direction = enum { + /// This value sets inline base direction (bidi directionality) to line-left-to-line-right. + ltr, + /// This value sets inline base direction (bidi directionality) to line-right-to-line-left. + rtl, + + pub usingnamespace css.DefineEnumProperty(@This()); +}; /// A value for the [unicode-bidi](https://drafts.csswg.org/css-writing-modes-3/#unicode-bidi) property. pub const UnicodeBidi = css.DefineEnumProperty(@compileError(css.todo_stuff.depth)); diff --git a/src/css/properties/transform.zig b/src/css/properties/transform.zig index b549831dd1..576779ad30 100644 --- a/src/css/properties/transform.zig +++ b/src/css/properties/transform.zig @@ -47,12 +47,23 @@ pub const TransformList = struct { _ = dest; // autofix @panic(css.todo_stuff.depth); } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } }; /// An individual transform function (https://www.w3.org/TR/2019/CR-css-transforms-1-20190214/#two-d-transform-functions). pub const Transform = union(enum) { /// A 2D translation. - translate: struct { x: LengthPercentage, y: LengthPercentage }, + translate: struct { + x: LengthPercentage, + y: LengthPercentage, + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } + }, /// A translation in the X direction. translate_x: LengthPercentage, /// A translation in the Y direction. @@ -60,9 +71,24 @@ pub const Transform = union(enum) { /// A translation in the Z direction. translate_z: Length, /// A 3D translation. - translate_3d: struct { x: LengthPercentage, y: LengthPercentage, z: Length }, + translate_3d: struct { + x: LengthPercentage, + y: LengthPercentage, + z: Length, + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } + }, /// A 2D scale. - scale: struct { x: NumberOrPercentage, y: NumberOrPercentage }, + scale: struct { + x: NumberOrPercentage, + y: NumberOrPercentage, + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } + }, /// A scale in the X direction. scale_x: NumberOrPercentage, /// A scale in the Y direction. @@ -70,7 +96,15 @@ pub const Transform = union(enum) { /// A scale in the Z direction. scale_z: NumberOrPercentage, /// A 3D scale. - scale_3d: struct { x: NumberOrPercentage, y: NumberOrPercentage, z: NumberOrPercentage }, + scale_3d: struct { + x: NumberOrPercentage, + y: NumberOrPercentage, + z: NumberOrPercentage, + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } + }, /// A 2D rotation. rotate: Angle, /// A rotation around the X axis. @@ -80,9 +114,25 @@ pub const Transform = union(enum) { /// A rotation around the Z axis. rotate_z: Angle, /// A 3D rotation. - rotate_3d: struct { x: f32, y: f32, z: f32, angle: Angle }, + rotate_3d: struct { + x: f32, + y: f32, + z: f32, + angle: Angle, + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } + }, /// A 2D skew. - skew: struct { x: Angle, y: Angle }, + skew: struct { + x: Angle, + y: Angle, + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } + }, /// A skew along the X axis. skew_x: Angle, /// A skew along the Y axis. @@ -104,6 +154,10 @@ pub const Transform = union(enum) { _ = dest; // autofix @panic(css.todo_stuff.depth); } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } }; /// A 2D matrix. @@ -115,6 +169,14 @@ pub fn Matrix(comptime T: type) type { d: T, e: T, f: T, + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } }; } diff --git a/src/css/rules/container.zig b/src/css/rules/container.zig index f158ea1ae6..13a11ca966 100644 --- a/src/css/rules/container.zig +++ b/src/css/rules/container.zig @@ -39,6 +39,10 @@ pub const ContainerName = struct { pub fn toCss(this: *const This, comptime W: type, dest: *Printer(W)) PrintErr!void { return try CustomIdentFns.toCss(&this.v, W, dest); } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } }; pub const ContainerNameFns = ContainerName; @@ -101,6 +105,10 @@ pub const StyleQuery = union(enum) { operator: css.media_query.Operator, /// The conditions for the operator. conditions: ArrayList(StyleQuery), + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } }, pub fn toCss(this: *const StyleQuery, comptime W: type, dest: *Printer(W)) PrintErr!void { @@ -175,6 +183,10 @@ pub const StyleQuery = union(enum) { pub fn parseStyleQuery(input: *css.Parser) Result(@This()) { return .{ .err = input.newErrorForNextToken() }; } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } }; pub const ContainerCondition = union(enum) { @@ -188,6 +200,10 @@ pub const ContainerCondition = union(enum) { operator: css.media_query.Operator, /// The conditions for the operator. conditions: ArrayList(ContainerCondition), + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } }, /// A style query. style: StyleQuery, @@ -286,6 +302,10 @@ pub const ContainerCondition = union(enum) { .style => false, }; } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } }; /// A [@container](https://drafts.csswg.org/css-contain-3/#container-rule) rule. @@ -327,5 +347,9 @@ pub fn ContainerRule(comptime R: type) type { try dest.newline(); try dest.writeChar('}'); } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) This { + return css.implementDeepClone(@This(), this, allocator); + } }; } diff --git a/src/css/rules/counter_style.zig b/src/css/rules/counter_style.zig index a8a3e10d8d..568aae137e 100644 --- a/src/css/rules/counter_style.zig +++ b/src/css/rules/counter_style.zig @@ -44,4 +44,8 @@ pub const CounterStyleRule = struct { try css.css_values.ident.CustomIdentFns.toCss(&this.name, W, dest); try this.declarations.toCssBlock(W, dest); } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) This { + return css.implementDeepClone(@This(), this, allocator); + } }; diff --git a/src/css/rules/custom_media.zig b/src/css/rules/custom_media.zig index 854abb2807..cc0d7d363e 100644 --- a/src/css/rules/custom_media.zig +++ b/src/css/rules/custom_media.zig @@ -21,6 +21,14 @@ pub const CustomMediaRule = struct { const This = @This(); + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) This { + return This{ + .name = this.name, + .query = this.query.deepClone(allocator), + .loc = this.loc, + }; + } + pub fn toCss(this: *const This, comptime W: type, dest: *Printer(W)) PrintErr!void { // #[cfg(feature = "sourcemap")] // dest.add_mapping(self.loc); diff --git a/src/css/rules/document.zig b/src/css/rules/document.zig index 2ace5662ed..485aef4464 100644 --- a/src/css/rules/document.zig +++ b/src/css/rules/document.zig @@ -51,5 +51,9 @@ pub fn MozDocumentRule(comptime R: type) type { try dest.newline(); try dest.writeChar('}'); } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) This { + return css.implementDeepClone(@This(), this, allocator); + } }; } diff --git a/src/css/rules/font_face.zig b/src/css/rules/font_face.zig index e0a2408025..2867b2ca64 100644 --- a/src/css/rules/font_face.zig +++ b/src/css/rules/font_face.zig @@ -89,6 +89,10 @@ pub const FontFaceProperty = union(enum) { }, }; } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) This { + return css.implementDeepClone(@This(), this, allocator); + } }; /// A contiguous range of Unicode code points. @@ -416,6 +420,10 @@ pub const FontFormat = union(enum) { .string => try dest.writeStr(this.string), } } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } }; /// A value for the [src](https://drafts.csswg.org/css-fonts/#src-desc) @@ -461,6 +469,10 @@ pub const Source = union(enum) { }, } } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } }; pub const FontTechnology = enum { @@ -583,6 +595,10 @@ pub const UrlSource = struct { try dest.writeChar(')'); } } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } }; /// A [@font-face](https://drafts.csswg.org/css-fonts/#font-face-rule) rule. @@ -614,6 +630,10 @@ pub const FontFaceRule = struct { try dest.newline(); try dest.writeChar('}'); } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) This { + return css.implementDeepClone(@This(), this, allocator); + } }; pub const FontFaceDeclarationParser = struct { diff --git a/src/css/rules/font_palette_values.zig b/src/css/rules/font_palette_values.zig index 1f33c44e75..d5f1eb0c1b 100644 --- a/src/css/rules/font_palette_values.zig +++ b/src/css/rules/font_palette_values.zig @@ -75,6 +75,10 @@ pub const FontPaletteValuesRule = struct { try dest.newline(); try dest.writeChar('}'); } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) This { + return css.implementDeepClone(@This(), this, allocator); + } }; pub const FontPaletteValuesProperty = union(enum) { @@ -119,6 +123,10 @@ pub const FontPaletteValuesProperty = union(enum) { }, } } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } }; /// A value for the [override-colors](https://drafts.csswg.org/css-fonts-4/#override-color) @@ -156,6 +164,10 @@ pub const OverrideColors = struct { try dest.writeChar(' '); try this.color.toCss(W, dest); } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } }; /// A value for the [base-palette](https://drafts.csswg.org/css-fonts-4/#base-palette-desc) @@ -195,6 +207,10 @@ pub const BasePalette = union(enum) { .integer => try css.CSSIntegerFns.toCss(&@as(i32, @intCast(this.integer)), W, dest), } } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } }; pub const FontPaletteValuesDeclarationParser = struct { diff --git a/src/css/rules/import.zig b/src/css/rules/import.zig index 30cda65171..7c42e67834 100644 --- a/src/css/rules/import.zig +++ b/src/css/rules/import.zig @@ -65,6 +65,10 @@ pub const ImportRule = struct { layer: ?struct { /// PERF: null pointer optimizaiton, nullable v: ?LayerName, + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } }, /// An optional `supports()` condition. @@ -167,4 +171,8 @@ pub const ImportRule = struct { } try dest.writeStr(";"); } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) This { + return css.implementDeepClone(@This(), this, allocator); + } }; diff --git a/src/css/rules/keyframes.zig b/src/css/rules/keyframes.zig index e4ad00a57b..640683d41a 100644 --- a/src/css/rules/keyframes.zig +++ b/src/css/rules/keyframes.zig @@ -166,6 +166,10 @@ pub const KeyframesName = union(enum) { }, } } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } }; pub const KeyframeSelector = union(enum) { @@ -205,6 +209,10 @@ pub const KeyframeSelector = union(enum) { }, } } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } }; /// An individual keyframe within an `@keyframes` rule. @@ -230,6 +238,10 @@ pub const Keyframe = struct { try this.declarations.toCssBlock(W, dest); } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } }; pub const KeyframesRule = struct { @@ -296,4 +308,8 @@ pub const KeyframesRule = struct { _ = targets; // autofix @panic(css.todo_stuff.depth); } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) This { + return css.implementDeepClone(@This(), this, allocator); + } }; diff --git a/src/css/rules/layer.zig b/src/css/rules/layer.zig index 5208fead78..05c2d692a8 100644 --- a/src/css/rules/layer.zig +++ b/src/css/rules/layer.zig @@ -38,14 +38,14 @@ pub const LayerName = struct { pub fn deepClone(this: *const LayerName, allocator: std.mem.Allocator) LayerName { return LayerName{ - .v = this.v.clone(allocator) catch bun.outOfMemory(), + .v = this.v.clone(allocator), }; } pub fn eql(lhs: *const LayerName, rhs: *const LayerName) bool { - if (lhs.v.items.len != rhs.v.items.len) return false; - for (lhs.v.items, 0..) |part, i| { - if (!bun.strings.eql(part, rhs.v.items[i])) return false; + if (lhs.v.len() != rhs.v.len()) return false; + for (lhs.v.slice(), 0..) |part, i| { + if (!bun.strings.eql(part, rhs.v.at(@intCast(i)).*)) return false; } return true; } @@ -59,7 +59,7 @@ pub const LayerName = struct { parts.append( input.allocator(), ident, - ) catch bun.outOfMemory(); + ); while (true) { const Fn = struct { @@ -101,7 +101,7 @@ pub const LayerName = struct { parts.append( input.allocator(), name, - ) catch bun.outOfMemory(); + ); } return .{ .result = LayerName{ .v = parts } }; @@ -110,7 +110,7 @@ pub const LayerName = struct { pub fn toCss(this: *const LayerName, comptime W: type, dest: *css.Printer(W)) css.PrintErr!void { var first = true; - for (this.v.items) |name| { + for (this.v.slice()) |name| { if (first) { first = false; } else { @@ -154,6 +154,10 @@ pub fn LayerBlockRule(comptime R: type) type { try dest.newline(); try dest.writeChar('}'); } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) This { + return css.implementDeepClone(@This(), this, allocator); + } }; } @@ -175,4 +179,8 @@ pub const LayerStatementRule = struct { try css.to_css.fromList(LayerName, &this.names, W, dest); try dest.writeChar(';'); } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) This { + return css.implementDeepClone(@This(), this, allocator); + } }; diff --git a/src/css/rules/media.zig b/src/css/rules/media.zig index 7900415560..da1f5e1898 100644 --- a/src/css/rules/media.zig +++ b/src/css/rules/media.zig @@ -24,7 +24,7 @@ pub fn MediaRule(comptime R: type) type { const This = @This(); - pub fn minify(this: *This, context: *css.MinifyContext, parent_is_unused: bool) Maybe(bool, css.MinifyError) { + pub fn minify(this: *This, context: *css.MinifyContext, parent_is_unused: bool) css.MinifyErr!bool { _ = this; // autofix _ = context; // autofix _ = parent_is_unused; // autofix @@ -50,5 +50,9 @@ pub fn MediaRule(comptime R: type) type { try dest.newline(); return dest.writeChar('}'); } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) This { + return css.implementDeepClone(@This(), this, allocator); + } }; } diff --git a/src/css/rules/namespace.zig b/src/css/rules/namespace.zig index b3caf037ed..30bdade77f 100644 --- a/src/css/rules/namespace.zig +++ b/src/css/rules/namespace.zig @@ -34,4 +34,8 @@ pub const NamespaceRule = struct { try css.css_values.string.CSSStringFns.toCss(&this.url, W, dest); try dest.writeChar(':'); } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) This { + return css.implementDeepClone(@This(), this, allocator); + } }; diff --git a/src/css/rules/nesting.zig b/src/css/rules/nesting.zig index 90db3b8c91..9aceb97b51 100644 --- a/src/css/rules/nesting.zig +++ b/src/css/rules/nesting.zig @@ -30,5 +30,9 @@ pub fn NestingRule(comptime R: type) type { } return try this.style.toCss(W, dest); } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) This { + return css.implementDeepClone(@This(), this, allocator); + } }; } diff --git a/src/css/rules/page.zig b/src/css/rules/page.zig index ec8806c88d..267c49f8c5 100644 --- a/src/css/rules/page.zig +++ b/src/css/rules/page.zig @@ -84,6 +84,10 @@ pub const PageSelector = struct { try pseudo.toCss(W, dest); } } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } }; pub const PageMarginRule = struct { @@ -104,6 +108,10 @@ pub const PageMarginRule = struct { try this.margin_box.toCss(W, dest); try this.declarations.toCssBlock(W, dest); } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } }; /// A [@page](https://www.w3.org/TR/css-page-3/#at-page-rule) rule. @@ -214,6 +222,10 @@ pub const PageRule = struct { try dest.newline(); try dest.writeChar('}'); } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) This { + return css.implementDeepClone(@This(), this, allocator); + } }; /// A page pseudo class within an `@page` selector. @@ -242,6 +254,10 @@ pub const PagePseudoClass = enum { pub fn toCss(this: *const @This(), comptime W: type, dest: *Printer(W)) PrintErr!void { return css.enum_property_util.toCss(@This(), this, W, dest); } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } }; /// A [page margin box](https://www.w3.org/TR/css-page-3/#margin-boxes). diff --git a/src/css/rules/property.zig b/src/css/rules/property.zig index 3e9f2feb49..b3044d1836 100644 --- a/src/css/rules/property.zig +++ b/src/css/rules/property.zig @@ -125,6 +125,10 @@ pub const PropertyRule = struct { try dest.newline(); try dest.writeChar(';'); } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) This { + return css.implementDeepClone(@This(), this, allocator); + } }; pub const PropertyRuleDeclarationParser = struct { diff --git a/src/css/rules/rules.zig b/src/css/rules/rules.zig index f965f878c6..b0c51b97dd 100644 --- a/src/css/rules/rules.zig +++ b/src/css/rules/rules.zig @@ -37,6 +37,8 @@ pub const scope = @import("./scope.zig"); pub const media = @import("./media.zig"); pub const starting_style = @import("./starting_style.zig"); +const debug = bun.Output.scoped(.CSS_MINIFY, false); + pub fn CssRule(comptime Rule: type) type { return union(enum) { /// A `@media` rule. @@ -115,6 +117,10 @@ pub fn CssRule(comptime Rule: type) type { .ignored => {}, }; } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) This { + return css.implementDeepClone(@This(), this, allocator); + } }; } @@ -124,13 +130,14 @@ pub fn CssRuleList(comptime AtRule: type) type { const This = @This(); - pub fn minify(this: *This, context: *MinifyContext, parent_is_unused: bool) Maybe(void, css.MinifyError) { - var keyframe_rules: keyframes.KeyframesName.HashMap(usize) = .{}; - const layer_rules: layer.LayerName.HashMap(usize) = .{}; - _ = layer_rules; // autofix - const property_rules: css.css_values.ident.DashedIdent.HashMap(usize) = .{}; - _ = property_rules; // autofix - // const style_rules = void; + pub fn minify(this: *This, context: *MinifyContext, parent_is_unused: bool) css.MinifyErr!void { + // var keyframe_rules: keyframes.KeyframesName.HashMap(usize) = .{}; + // _ = keyframe_rules; // autofix + // const layer_rules: layer.LayerName.HashMap(usize) = .{}; + // _ = layer_rules; // autofix + // const property_rules: css.css_values.ident.DashedIdent.HashMap(usize) = .{}; + // _ = property_rules; // autofix + var style_rules = StyleRuleKey(AtRule).HashMap(usize){}; // _ = style_rules; // autofix var rules = ArrayList(CssRule(AtRule)){}; @@ -138,46 +145,49 @@ pub fn CssRuleList(comptime AtRule: type) type { // NOTE Anytime you append to `rules` with this `rule`, you must set `moved_rule` to true. var moved_rule = false; defer if (moved_rule) { + // PERF calling deinit here might allow mimalloc to reuse the freed memory rule.* = .ignored; }; switch (rule.*) { .keyframes => |*keyframez| { - if (context.unused_symbols.contains(switch (keyframez.name) { - .ident => |ident| ident, - .custom => |custom| custom, - })) { - continue; - } + _ = keyframez; // autofix + // if (context.unused_symbols.contains(switch (keyframez.name) { + // .ident => |ident| ident.v, + // .custom => |custom| custom, + // })) { + // continue; + // } - keyframez.minify(context); + // keyframez.minify(context); - // Merge @keyframes rules with the same name. - if (keyframe_rules.get(keyframez.name)) |existing_idx| { - if (existing_idx < rules.items.len and rules.items[existing_idx] == .keyframes) { - var existing = &rules.items[existing_idx].keyframes; - // If the existing rule has the same vendor prefixes, replace it with this rule. - if (existing.vendor_prefix.eq(keyframez.vendor_prefix)) { - existing.* = keyframez.clone(context.allocator); - continue; - } - // Otherwise, if the keyframes are identical, merge the prefixes. - if (existing.keyframes == keyframez.keyframes) { - existing.vendor_prefix |= keyframez.vendor_prefix; - existing.vendor_prefix = context.targets.prefixes(existing.vendor_prefix, css.prefixes.Feature.at_keyframes); - continue; - } - } - } + // // Merge @keyframes rules with the same name. + // if (keyframe_rules.get(keyframez.name)) |existing_idx| { + // if (existing_idx < rules.items.len and rules.items[existing_idx] == .keyframes) { + // var existing = &rules.items[existing_idx].keyframes; + // // If the existing rule has the same vendor prefixes, replace it with this rule. + // if (existing.vendor_prefix.eq(keyframez.vendor_prefix)) { + // existing.* = keyframez.clone(context.allocator); + // continue; + // } + // // Otherwise, if the keyframes are identical, merge the prefixes. + // if (existing.keyframes == keyframez.keyframes) { + // existing.vendor_prefix |= keyframez.vendor_prefix; + // existing.vendor_prefix = context.targets.prefixes(existing.vendor_prefix, css.prefixes.Feature.at_keyframes); + // continue; + // } + // } + // } - keyframez.vendor_prefix = context.targets.prefixes(keyframez.vendor_prefix, css.prefixes.Feature.at_keyframes); - keyframe_rules.put(context.allocator, keyframez.name, rules.items.len) catch bun.outOfMemory(); + // keyframez.vendor_prefix = context.targets.prefixes(keyframez.vendor_prefix, css.prefixes.Feature.at_keyframes); + // keyframe_rules.put(context.allocator, keyframez.name, rules.items.len) catch bun.outOfMemory(); - const fallbacks = keyframez.getFallbacks(AtRule, context.targets); - moved_rule = true; - rules.append(context.allocator, rule.*) catch bun.outOfMemory(); - rules.appendSlice(context.allocator, fallbacks) catch bun.outOfMemory(); - continue; + // const fallbacks = keyframez.getFallbacks(AtRule, context.targets); + // moved_rule = true; + // rules.append(context.allocator, rule.*) catch bun.outOfMemory(); + // rules.appendSlice(context.allocator, fallbacks) catch bun.outOfMemory(); + // continue; + debug("TODO: KeyframesRule", .{}); }, .custom_media => { if (context.custom_media != null) { @@ -185,19 +195,17 @@ pub fn CssRuleList(comptime AtRule: type) type { } }, .media => |*med| { + moved_rule = false; if (rules.items[rules.items.len - 1] == .media) { var last_rule = &rules.items[rules.items.len - 1].media; if (last_rule.query.eql(&med.query)) { last_rule.rules.v.appendSlice(context.allocator, med.rules.v.items) catch bun.outOfMemory(); - if (last_rule.minify(context, parent_is_unused).asErr()) |e| { - return .{ .err = e }; - } + _ = try last_rule.minify(context, parent_is_unused); continue; } - switch (med.minify(context, parent_is_unused)) { - .result => continue, - .err => |e| return .{ .err = e }, + if (try med.minify(context, parent_is_unused)) { + continue; } } }, @@ -209,41 +217,240 @@ pub fn CssRuleList(comptime AtRule: type) type { } } - if (supp.minify(context, parent_is_unused).asErr()) |e| return .{ .err = e }; + try supp.minify(context, parent_is_unused); if (supp.rules.v.items.len == 0) continue; }, .container => |*cont| { _ = cont; // autofix + debug("TODO: ContainerRule", .{}); }, .layer_block => |*lay| { _ = lay; // autofix + debug("TODO: LayerBlockRule", .{}); }, .layer_statement => |*lay| { _ = lay; // autofix + debug("TODO: LayerStatementRule", .{}); }, .moz_document => |*doc| { _ = doc; // autofix + debug("TODO: MozDocumentRule", .{}); }, .style => |*sty| { - _ = sty; // autofix + const Selector = css.selector.Selector; + const SelectorList = css.selector.SelectorList; + const Component = css.selector.Component; + if (parent_is_unused or try sty.minify(context, parent_is_unused)) { + continue; + } + + // If some of the selectors in this rule are not compatible with the targets, + // we need to either wrap in :is() or split them into multiple rules. + var incompatible: css.SmallList(css.selector.parser.Selector, 1) = if (sty.selectors.v.len() > 1 and + context.targets.shouldCompileSelectors() and + !sty.isCompatible(context.targets.*)) + incompatible: { + // The :is() selector accepts a forgiving selector list, so use that if possible. + // Note that :is() does not allow pseudo elements, so we need to check for that. + // In addition, :is() takes the highest specificity of its arguments, so if the selectors + // have different weights, we need to split them into separate rules as well. + if (context.targets.isCompatible(css.compat.Feature.is_selector) and !sty.selectors.anyHasPseudoElement() and sty.selectors.specifitiesAllEqual()) { + const component = Component{ .is = sty.selectors.v.toOwnedSlice(context.allocator) }; + var list = css.SmallList(css.selector.parser.Selector, 1){}; + list.append(context.allocator, Selector.fromComponent(context.allocator, component)); + sty.selectors = SelectorList{ + .v = list, + }; + break :incompatible css.SmallList(Selector, 1){}; + } else { + // Otherwise, partition the selectors and keep the compatible ones in this rule. + // We will generate additional rules for incompatible selectors later. + var incompatible = css.SmallList(Selector, 1){}; + var i: u32 = 0; + while (i < sty.selectors.v.len()) { + if (css.selector.isCompatible(sty.selectors.v.slice()[i .. i + 1], context.targets.*)) { + i += 1; + } else { + // Move the selector to the incompatible list. + incompatible.append( + context.allocator, + sty.selectors.v.orderedRemove(i), + ); + } + } + break :incompatible incompatible; + } + } else .{}; + + sty.updatePrefix(context); + + // Attempt to merge the new rule with the last rule we added. + var merged = false; + const ZACK_REMOVE_THIS = false; + _ = ZACK_REMOVE_THIS; // autofix + if (rules.items.len > 0 and rules.items[rules.items.len - 1] == .style) { + const last_style_rule = &rules.items[rules.items.len - 1].style; + if (mergeStyleRules(AtRule, sty, last_style_rule, context)) { + // If that was successful, then the last rule has been updated to include the + // selectors/declarations of the new rule. This might mean that we can merge it + // with the previous rule, so continue trying while we have style rules available. + while (rules.items.len >= 2) { + const len = rules.items.len; + var a, var b = bun.splitAtMut(CssRule(AtRule), rules.items, len - 1); + if (b[0] == .style and a[len - 2] == .style) { + if (mergeStyleRules(AtRule, &b[0].style, &a[len - 2].style, context)) { + // If we were able to merge the last rule into the previous one, remove the last. + const popped = rules.pop(); + _ = popped; // autofix + // TODO: deinit? + // popped.deinit(contet.allocator); + continue; + } + } + // If we didn't see a style rule, or were unable to merge, stop. + break; + } + merged = true; + } + } + + // Create additional rules for logical properties, @supports overrides, and incompatible selectors. + const supps = context.handler_context.getSupportsRules(AtRule, sty); + const logical = context.handler_context.getAdditionalRules(AtRule, sty); + const StyleRule = style.StyleRule(AtRule); + + const IncompatibleRuleEntry = struct { rule: StyleRule, supports: ArrayList(css.CssRule(AtRule)), logical: ArrayList(css.CssRule(AtRule)) }; + var incompatible_rules: css.SmallList(IncompatibleRuleEntry, 1) = incompatible_rules: { + var incompatible_rules = css.SmallList(IncompatibleRuleEntry, 1).initCapacity( + context.allocator, + incompatible.len(), + ); + + for (incompatible.slice_mut()) |sel| { + // Create a clone of the rule with only the one incompatible selector. + const list = SelectorList{ .v = css.SmallList(Selector, 1).withOne(sel) }; + var clone: StyleRule = .{ + .selectors = list, + .vendor_prefix = sty.vendor_prefix, + .declarations = sty.declarations.deepClone(context.allocator), + .rules = sty.rules.deepClone(context.allocator), + .loc = sty.loc, + }; + clone.updatePrefix(context); + + // Also add rules for logical properties and @supports overrides. + const s = context.handler_context.getSupportsRules(AtRule, &clone); + const l = context.handler_context.getAdditionalRules(AtRule, &clone); + incompatible_rules.append(context.allocator, IncompatibleRuleEntry{ + .rule = clone, + .supports = s, + .logical = l, + }); + } + + break :incompatible_rules incompatible_rules; + }; + defer incompatible.deinit(context.allocator); + defer incompatible_rules.deinit(context.allocator); + + context.handler_context.reset(); + + // If the rule has nested rules, and we have extra rules to insert such as for logical properties, + // we need to split the rule in two so we can insert the extra rules in between the declarations from + // the main rule and the nested rules. + const nested_rule: ?StyleRule = if (sty.rules.v.items.len > 0 and + // can happen if there are no compatible rules, above. + sty.selectors.v.len() > 0 and + (logical.items.len > 0 or supps.items.len > 0 or !incompatible_rules.isEmpty())) + brk: { + var rulesss: CssRuleList(AtRule) = .{}; + std.mem.swap(CssRuleList(AtRule), &sty.rules, &rulesss); + break :brk StyleRule{ + .selectors = sty.selectors.deepClone(context.allocator), + .declarations = css.DeclarationBlock{}, + .rules = rulesss, + .vendor_prefix = sty.vendor_prefix, + .loc = sty.loc, + }; + } else null; + + if (!merged and !sty.isEmpty()) { + const source_index = sty.loc.source_index; + const has_no_rules = sty.rules.v.items.len == 0; + const idx = rules.items.len; + + rules.append(context.allocator, rule.*) catch bun.outOfMemory(); + moved_rule = true; + + // Check if this rule is a duplicate of an earlier rule, meaning it has + // the same selectors and defines the same properties. If so, remove the + // earlier rule because this one completely overrides it. + if (has_no_rules) { + const key = StyleRuleKey(AtRule).new(&rules, idx); + if (idx > 0) { + if (style_rules.fetchSwapRemove(key)) |i_| { + const i = i_.value; + if (i < rules.items.len and rules.items[i] == .style) { + const other = &rules.items[i].style; + // Don't remove the rule if this is a CSS module and the other rule came from a different file. + if (!context.css_modules or source_index == other.loc.source_index) { + // Only mark the rule as ignored so we don't need to change all of the indices. + rules.items[i] = .ignored; + } + } + } + } + + style_rules.put(context.allocator, key, idx) catch bun.outOfMemory(); + } + } + + if (logical.items.len > 0) { + var log = CssRuleList(AtRule){ .v = logical }; + try log.minify(context, parent_is_unused); + rules.appendSlice(context.allocator, log.v.items) catch bun.outOfMemory(); + } + rules.appendSlice(context.allocator, supps.items) catch bun.outOfMemory(); + for (incompatible_rules.slice_mut()) |incompatible_entry| { + if (!incompatible_entry.rule.isEmpty()) { + rules.append(context.allocator, .{ .style = incompatible_entry.rule }) catch bun.outOfMemory(); + } + if (incompatible_entry.logical.items.len > 0) { + var log = CssRuleList(AtRule){ .v = incompatible_entry.logical }; + try log.minify(context, parent_is_unused); + rules.appendSlice(context.allocator, log.v.items) catch bun.outOfMemory(); + } + rules.appendSlice(context.allocator, incompatible_entry.supports.items) catch bun.outOfMemory(); + } + if (nested_rule) |nested| { + rules.append(context.allocator, .{ .style = nested }) catch bun.outOfMemory(); + } + + continue; }, .counter_style => |*cntr| { _ = cntr; // autofix + debug("TODO: CounterStyleRule", .{}); }, .scope => |*scpe| { _ = scpe; // autofix + debug("TODO: ScopeRule", .{}); }, .nesting => |*nst| { _ = nst; // autofix + debug("TODO: NestingRule", .{}); }, .starting_style => |*rl| { _ = rl; // autofix + debug("TODO: StartingStyleRule", .{}); }, .font_palette_values => |*f| { _ = f; // autofix + debug("TODO: FontPaletteValuesRule", .{}); }, .property => |*prop| { _ = prop; // autofix + debug("TODO: PropertyRule", .{}); }, else => {}, } @@ -255,7 +462,7 @@ pub fn CssRuleList(comptime AtRule: type) type { css.deepDeinit(CssRule(AtRule), context.allocator, &this.v); this.v = rules; - return .{ .result = {} }; + return; } pub fn toCss(this: *const This, comptime W: type, dest: *Printer(W)) PrintErr!void { @@ -294,10 +501,15 @@ pub fn CssRuleList(comptime AtRule: type) type { last_without_block = rule.* == .import or rule.* == .namespace or rule.* == .layer_statement; } } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } }; } pub const MinifyContext = struct { + /// NOTE: this should the same allocator the AST was allocated with allocator: std.mem.Allocator, targets: *const css.targets.Targets, handler: *css.DeclarationHandler, @@ -306,6 +518,7 @@ pub const MinifyContext = struct { unused_symbols: *const std.StringArrayHashMapUnmanaged(void), custom_media: ?std.StringArrayHashMapUnmanaged(custom_media.CustomMediaRule), css_modules: bool, + err: ?css.MinifyError = null, }; pub const Location = struct { @@ -338,21 +551,36 @@ pub fn StyleRuleKey(comptime R: type) type { return struct { list: *const ArrayList(CssRule(R)), index: usize, + // TODO: store in the hashmap by setting `store_hash` to true hash: u64, const This = @This(); pub fn HashMap(comptime V: type) type { - return std.ArrayHashMapUnmanaged(StyleRuleKey(R), V, struct { - pub fn hash(_: @This(), key: This) u32 { - _ = key; // autofix - @panic("TODO"); - } + return std.ArrayHashMapUnmanaged( + StyleRuleKey(R), + V, + struct { + pub fn hash(_: @This(), key: This) u32 { + return @intCast(key.hash); + } - pub fn eql(_: @This(), a: This, b: This, _: usize) bool { - return a.eql(&b); - } - }); + pub fn eql(_: @This(), a: This, b: This, _: usize) bool { + return a.eql(&b); + } + }, + // TODO: make this true + false, + ); + } + + pub fn new(list: *const ArrayList(CssRule(R)), index: usize) This { + const rule = &list.items[index].style; + return This{ + .list = list, + .index = index, + .hash = rule.hashKey(), + }; } pub fn eql(this: *const This, other: *const This) bool { @@ -370,3 +598,73 @@ pub fn StyleRuleKey(comptime R: type) type { } }; } + +fn mergeStyleRules( + comptime T: type, + sty: *style.StyleRule(T), + last_style_rule: *style.StyleRule(T), + context: *MinifyContext, +) bool { + // Merge declarations if the selectors are equivalent, and both are compatible with all targets. + if (sty.selectors.eql(&last_style_rule.selectors) and + sty.isCompatible(context.targets.*) and + last_style_rule.isCompatible(context.targets.*) and + sty.rules.v.items.len == 0 and + last_style_rule.rules.v.items.len == 0 and + (!context.css_modules or sty.loc.source_index == last_style_rule.loc.source_index)) + { + last_style_rule.declarations.declarations.appendSlice( + context.allocator, + sty.declarations.declarations.items, + ) catch bun.outOfMemory(); + sty.declarations.declarations.clearRetainingCapacity(); + + last_style_rule.declarations.important_declarations.appendSlice( + context.allocator, + sty.declarations.important_declarations.items, + ) catch bun.outOfMemory(); + sty.declarations.important_declarations.clearRetainingCapacity(); + + last_style_rule.declarations.minify( + context.handler, + context.important_handler, + &context.handler_context, + ); + return true; + } else if (sty.declarations.eql(&last_style_rule.declarations) and + sty.rules.v.items.len == 0 and + last_style_rule.rules.v.items.len == 0) + { + // If both selectors are potentially vendor prefixable, and they are + // equivalent minus prefixes, add the prefix to the last rule. + if (!sty.vendor_prefix.isEmpty() and + !last_style_rule.vendor_prefix.isEmpty() and + css.selector.isEquivalent(sty.selectors.v.slice(), last_style_rule.selectors.v.slice())) + { + // If the new rule is unprefixed, replace the prefixes of the last rule. + // Otherwise, add the new prefix. + if (sty.vendor_prefix.contains(css.VendorPrefix{ .none = true }) and context.targets.shouldCompileSelectors()) { + last_style_rule.vendor_prefix = sty.vendor_prefix; + } else { + last_style_rule.vendor_prefix.insert(sty.vendor_prefix); + } + return true; + } + + // Append the selectors to the last rule if the declarations are the same, and all selectors are compatible. + if (sty.isCompatible(context.targets.*) and last_style_rule.isCompatible(context.targets.*)) { + last_style_rule.selectors.v.appendSlice( + context.allocator, + sty.selectors.v.slice(), + ); + sty.selectors.v.clearRetainingCapacity(); + if (sty.vendor_prefix.contains(css.VendorPrefix{ .none = true }) and context.targets.shouldCompileSelectors()) { + last_style_rule.vendor_prefix = sty.vendor_prefix; + } else { + last_style_rule.vendor_prefix.insert(sty.vendor_prefix); + } + return true; + } + } + return false; +} diff --git a/src/css/rules/scope.zig b/src/css/rules/scope.zig index 93f69a7885..51436f416a 100644 --- a/src/css/rules/scope.zig +++ b/src/css/rules/scope.zig @@ -40,7 +40,7 @@ pub fn ScopeRule(comptime R: type) type { if (this.scope_start) |*scope_start| { try dest.writeChar('('); // try scope_start.toCss(W, dest); - try css.selector.serialize.serializeSelectorList(scope_start.v.items, W, dest, dest.context(), false); + try css.selector.serialize.serializeSelectorList(scope_start.v.slice(), W, dest, dest.context(), false); try dest.writeChar(')'); try dest.whitespace(); } @@ -54,11 +54,11 @@ pub fn ScopeRule(comptime R: type) type { if (this.scope_start) |*scope_start| { try dest.withContext(scope_start, scope_end, struct { pub fn toCssFn(scope_end_: *const css.selector.parser.SelectorList, comptime WW: type, d: *Printer(WW)) PrintErr!void { - return css.selector.serialize.serializeSelectorList(scope_end_.v.items, WW, d, d.context(), false); + return css.selector.serialize.serializeSelectorList(scope_end_.v.slice(), WW, d, d.context(), false); } }.toCssFn); } else { - return css.selector.serialize.serializeSelectorList(scope_end.v.items, W, dest, dest.context(), false); + return css.selector.serialize.serializeSelectorList(scope_end.v.slice(), W, dest, dest.context(), false); } try dest.writeChar(')'); try dest.whitespace(); @@ -74,5 +74,9 @@ pub fn ScopeRule(comptime R: type) type { try dest.newline(); try dest.writeChar('}'); } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) This { + return css.implementDeepClone(@This(), this, allocator); + } }; } diff --git a/src/css/rules/starting_style.zig b/src/css/rules/starting_style.zig index 54a7409213..f86a656931 100644 --- a/src/css/rules/starting_style.zig +++ b/src/css/rules/starting_style.zig @@ -37,5 +37,9 @@ pub fn StartingStyleRule(comptime R: type) type { try dest.newline(); try dest.writeChar('}'); } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) This { + return css.implementDeepClone(@This(), this, allocator); + } }; } diff --git a/src/css/rules/style.zig b/src/css/rules/style.zig index fe91f5fd56..e8d2bbe944 100644 --- a/src/css/rules/style.zig +++ b/src/css/rules/style.zig @@ -1,5 +1,6 @@ const std = @import("std"); pub const css = @import("../css_parser.zig"); +const bun = @import("root").bun; const ArrayList = std.ArrayListUnmanaged; const MediaList = css.MediaList; const CustomMedia = css.CustomMedia; @@ -31,6 +32,43 @@ pub fn StyleRule(comptime R: type) type { const This = @This(); + /// Returns whether the rule is empty. + pub fn isEmpty(this: *const This) bool { + return this.selectors.v.isEmpty() or (this.declarations.isEmpty() and this.rules.v.items.len == 0); + } + + /// Returns a hash of this rule for use when deduplicating. + /// Includes the selectors and properties. + pub fn hashKey(this: *const This) u64 { + var hasher = std.hash.Wyhash.init(0); + this.selectors.hash(&hasher); + this.declarations.hashPropertyIds(&hasher); + return hasher.final(); + } + + pub fn deepClone(this: *const This, allocator: std.mem.Allocator) This { + return This{ + .selectors = this.selectors.deepClone(allocator), + .vendor_prefix = this.vendor_prefix, + .declarations = this.declarations.deepClone(allocator), + .rules = this.rules.deepClone(allocator), + .loc = this.loc, + }; + } + + pub fn updatePrefix(this: *This, context: *css.MinifyContext) void { + this.vendor_prefix = css.selector.getPrefix(&this.selectors); + if (this.vendor_prefix.contains(css.VendorPrefix{ .none = true }) and + context.targets.shouldCompileSelectors()) + { + this.vendor_prefix = css.selector.downlevelSelectors(context.allocator, this.selectors.v.slice_mut(), context.targets.*); + } + } + + pub fn isCompatible(this: *const This, targets: css.targets.Targets) bool { + return css.selector.isCompatible(this.selectors.v.slice(), targets); + } + pub fn toCss(this: *const This, comptime W: type, dest: *Printer(W)) PrintErr!void { if (this.vendor_prefix.isEmpty()) { try this.toCssBase(W, dest); @@ -60,7 +98,7 @@ pub fn StyleRule(comptime R: type) type { fn toCssBase(this: *const This, comptime W: type, dest: *Printer(W)) PrintErr!void { // If supported, or there are no targets, preserve nesting. Otherwise, write nested rules after parent. const supports_nesting = this.rules.v.items.len == 0 or - css.Targets.shouldCompileSame( + !css.Targets.shouldCompileSame( &dest.targets, .nesting, ); @@ -72,7 +110,7 @@ pub fn StyleRule(comptime R: type) type { // #[cfg(feature = "sourcemap")] // dest.add_mapping(self.loc); - try css.selector.serialize.serializeSelectorList(this.selectors.v.items, W, dest, dest.context(), false); + try css.selector.serialize.serializeSelectorList(this.selectors.v.slice(), W, dest, dest.context(), false); try dest.whitespace(); try dest.writeChar('{'); dest.indent(); @@ -149,10 +187,58 @@ pub fn StyleRule(comptime R: type) type { } else { try Helpers.end(W, dest, has_declarations); try Helpers.newline(this, W, dest, supports_nesting, len); - try dest.withContext(&this.selectors, this, This.toCss); + try dest.withContext(&this.selectors, this, struct { + pub fn toCss(self: *const This, WW: type, d: *Printer(WW)) PrintErr!void { + return self.rules.toCss(WW, d); + } + }.toCss); } } + pub fn minify(this: *This, context: *css.MinifyContext, parent_is_unused: bool) css.MinifyErr!bool { + var unused = false; + if (context.unused_symbols.count() > 0) { + if (css.selector.isUnused(this.selectors.v.slice(), context.unused_symbols, parent_is_unused)) { + if (this.rules.v.items.len == 0) { + return true; + } + + this.declarations.declarations.clearRetainingCapacity(); + this.declarations.important_declarations.clearRetainingCapacity(); + unused = true; + } + } + + // TODO: this + // let pure_css_modules = context.pure_css_modules; + // if context.pure_css_modules { + // if !self.selectors.0.iter().all(is_pure_css_modules_selector) { + // return Err(MinifyError { + // kind: crate::error::MinifyErrorKind::ImpureCSSModuleSelector, + // loc: self.loc, + // }); + // } + + // // Parent rule contained id or class, so child rules don't need to. + // context.pure_css_modules = false; + // } + + context.handler_context.context = .style_rule; + this.declarations.minify(context.handler, context.important_handler, &context.handler_context); + context.handler_context.context = .none; + + if (this.rules.v.items.len > 0) { + var handler_context = context.handler_context.child(.style_rule); + std.mem.swap(css.PropertyHandlerContext, &context.handler_context, &handler_context); + try this.rules.minify(context, unused); + if (unused and this.rules.v.items.len == 0) { + return true; + } + } + + return false; + } + /// Returns whether this rule is a duplicate of another rule. /// This means it has the same selectors and properties. pub inline fn isDuplicate(this: *const This, other: *const This) bool { @@ -160,8 +246,11 @@ pub fn StyleRule(comptime R: type) type { this.selectors.eql(&other.selectors) and brk: { const len = @min(this.declarations.len(), other.declarations.len()); - for (this.declarations[0..len], other.declarations[0..len]) |*a, *b| { - if (!a.eql(b)) break :brk false; + for (this.declarations.declarations.items[0..len], other.declarations.declarations.items[0..len]) |*a, *b| { + if (!a.propertyId().eql(&b.propertyId())) break :brk false; + } + for (this.declarations.important_declarations.items[0..len], other.declarations.important_declarations.items[0..len]) |*a, *b| { + if (!a.propertyId().eql(&b.propertyId())) break :brk false; } break :brk true; }; diff --git a/src/css/rules/supports.zig b/src/css/rules/supports.zig index 4be232ebdc..4be36b14a1 100644 --- a/src/css/rules/supports.zig +++ b/src/css/rules/supports.zig @@ -43,6 +43,14 @@ pub const SupportsCondition = union(enum) { property_id: css.PropertyId, /// The raw value of the declaration. value: []const u8, + + 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); + } }, /// A selector to evaluate. @@ -51,10 +59,12 @@ pub const SupportsCondition = union(enum) { /// An unknown condition. unknown: []const u8, + pub fn eql(this: *const SupportsCondition, other: *const SupportsCondition) bool { + return css.implementEql(SupportsCondition, this, other); + } + pub fn deepClone(this: *const SupportsCondition, allocator: std.mem.Allocator) SupportsCondition { - _ = allocator; // autofix - _ = this; // autofix - @panic(css.todo_stuff.depth); + return css.implementDeepClone(SupportsCondition, this, allocator); } fn needsParens(this: *const SupportsCondition, parent: *const SupportsCondition) bool { @@ -246,7 +256,14 @@ pub const SupportsCondition = union(enum) { if (res.isOk()) return res; } }, - .open_curly => {}, + .open_paren => { + const res = input.tryParse(struct { + pub fn parseFn(i: *css.Parser) Result(SupportsCondition) { + return i.parseNestedBlock(SupportsCondition, {}, css.voidWrap(SupportsCondition, parse)); + } + }.parseFn, .{}); + if (res.isOk()) return res; + }, else => return .{ .err = location.newUnexpectedTokenError(tok.*) }, } @@ -379,11 +396,15 @@ pub fn SupportsRule(comptime R: type) type { try dest.writeChar('}'); } - pub fn minify(this: *This, context: *css.MinifyContext, parent_is_unused: bool) Maybe(void, css.MinifyError) { + pub fn minify(this: *This, context: *css.MinifyContext, parent_is_unused: bool) css.MinifyErr!void { _ = this; // autofix _ = context; // autofix _ = parent_is_unused; // autofix @panic(css.todo_stuff.depth); } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) This { + return css.implementDeepClone(@This(), this, allocator); + } }; } diff --git a/src/css/rules/unknown.zig b/src/css/rules/unknown.zig index 91da16a587..a1ab9408ff 100644 --- a/src/css/rules/unknown.zig +++ b/src/css/rules/unknown.zig @@ -48,4 +48,8 @@ pub const UnknownAtRule = struct { try dest.writeChar(';'); } } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) This { + return css.implementDeepClone(@This(), this, allocator); + } }; diff --git a/src/css/rules/viewport.zig b/src/css/rules/viewport.zig index 23c9e8e381..03f88aa8c5 100644 --- a/src/css/rules/viewport.zig +++ b/src/css/rules/viewport.zig @@ -31,4 +31,8 @@ pub const ViewportRule = struct { try dest.writeStr("viewport"); try this.declarations.toCssBlock(W, dest); } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) This { + return css.implementDeepClone(@This(), this, allocator); + } }; diff --git a/src/css/selectors/builder.zig b/src/css/selectors/builder.zig index fb96b46fb1..e07aef3eb7 100644 --- a/src/css/selectors/builder.zig +++ b/src/css/selectors/builder.zig @@ -89,26 +89,26 @@ pub fn SelectorBuilder(comptime Impl: type) type { /// Returns true if combinators have ever been pushed to this builder. pub inline fn hasCombinators(this: *This) bool { - return this.combinators.items.len > 0; + return this.combinators.len() > 0; } /// Completes the current compound selector and starts a new one, delimited /// by the given combinator. pub inline fn pushCombinator(this: *This, combinator: Combinator) void { - this.combinators.append(this.allocator, .{ combinator, this.current_len }) catch unreachable; + this.combinators.append(this.allocator, .{ combinator, this.current_len }); this.current_len = 0; } /// Pushes a simple selector onto the current compound selector. pub fn pushSimpleSelector(this: *This, ss: GenericComponent(Impl)) void { bun.assert(!ss.isCombinator()); - this.simple_selectors.append(this.allocator, ss) catch unreachable; + this.simple_selectors.append(this.allocator, ss); this.current_len += 1; } pub fn addNestingPrefix(this: *This) void { - this.combinators.insert(this.allocator, 0, .{ Combinator.descendant, 1 }) catch unreachable; - this.simple_selectors.insert(this.allocator, 0, .nesting) catch bun.outOfMemory(); + this.combinators.insert(this.allocator, 0, .{ Combinator.descendant, 1 }); + this.simple_selectors.insert(this.allocator, 0, .nesting); } pub fn deinit(this: *This) void { @@ -125,7 +125,7 @@ pub fn SelectorBuilder(comptime Impl: type) type { parsed_slotted: bool, parsed_part: bool, ) BuildResult { - const specifity = compute_specifity(Impl, this.simple_selectors.items); + const specifity = compute_specifity(Impl, this.simple_selectors.slice()); var flags = SelectorFlags.empty(); // PERF: is it faster to do these ORs all at once if (parsed_pseudo) { @@ -155,8 +155,8 @@ pub fn SelectorBuilder(comptime Impl: type) type { /// as the source. pub fn buildWithSpecificityAndFlags(this: *This, spec: SpecifityAndFlags) BuildResult { const T = GenericComponent(Impl); - const rest: []const T, const current: []const T = splitFromEnd(T, this.simple_selectors.items, this.current_len); - const combinators = this.combinators.items; + const rest: []const T, const current: []const T = splitFromEnd(T, this.simple_selectors.slice(), this.current_len); + const combinators = this.combinators.slice(); defer { // This function should take every component from `this.simple_selectors` // and place it into `components` and return it. @@ -165,14 +165,14 @@ pub fn SelectorBuilder(comptime Impl: type) type { // it is safe to just set the length to 0. // // Combinators don't need to be deinitialized because they are simple enums. - this.simple_selectors.items.len = 0; - this.combinators.items.len = 0; + this.simple_selectors.setLen(0); + this.combinators.setLen(0); } var components = ArrayList(T){}; var current_simple_selectors_i: usize = 0; - var combinator_i: i64 = @as(i64, @intCast(this.combinators.items.len)) - 1; + var combinator_i: i64 = @as(i64, @intCast(this.combinators.len())) - 1; var rest_of_simple_selectors = rest; var current_simple_selectors = current; diff --git a/src/css/selectors/parser.zig b/src/css/selectors/parser.zig index c981304a01..d633d9bd4a 100644 --- a/src/css/selectors/parser.zig +++ b/src/css/selectors/parser.zig @@ -13,6 +13,7 @@ pub const PrintErr = css.PrintErr; const Result = css.Result; const PrintResult = css.PrintResult; +const SmallList = css.SmallList; const ArrayList = std.ArrayListUnmanaged; const impl = css.selector.impl; @@ -53,6 +54,14 @@ pub const attrs = struct { return struct { prefix: Impl.SelectorImpl.NamespacePrefix, url: Impl.SelectorImpl.NamespaceUrl, + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } + + pub fn hash(this: *const @This(), hasher: *std.hash.Wyhash) void { + return css.implementHash(@This(), this, hasher); + } }; } @@ -64,6 +73,10 @@ pub const attrs = struct { operation: ParsedAttrSelectorOperation(Impl.SelectorImpl.AttrValue), never_matches: bool, + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } + pub fn toCss(this: *const @This(), comptime W: type, dest: *Printer(W)) PrintErr!void { try dest.writeChar('['); if (this.namespace) |nsp| switch (nsp) { @@ -95,6 +108,10 @@ pub const attrs = struct { } return dest.writeChar(']'); } + + pub fn hash(this: *const @This(), hasher: *std.hash.Wyhash) void { + return css.implementHash(@This(), this, hasher); + } }; } @@ -103,6 +120,14 @@ pub const attrs = struct { any, /// Empty string for no namespace specific: NamespaceUrl_, + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } + + pub fn hash(this: *const @This(), hasher: *std.hash.Wyhash) void { + return css.implementHash(@This(), this, hasher); + } }; } @@ -113,7 +138,21 @@ pub const attrs = struct { operator: AttrSelectorOperator, case_sensitivity: ParsedCaseSensitivity, expected_value: AttrValue, + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } + + pub fn __generateHash() void {} }, + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } + + pub fn hash(this: *const @This(), hasher: *std.hash.Wyhash) void { + return css.implementHash(@This(), this, hasher); + } }; } @@ -138,6 +177,10 @@ pub const attrs = struct { .suffix => "$=", }); } + + pub fn hash(this: *const @This(), hasher: *std.hash.Wyhash) void { + return css.implementHash(@This(), this, hasher); + } }; pub const AttrSelectorOperation = enum { @@ -339,6 +382,10 @@ fn parse_selector( } if (state.intersects(SelectorParsingState.AFTER_PSEUDO)) { + const source_location = input.currentSourceLocation(); + if (input.next().asValue()) |next| { + return .{ .err = source_location.newCustomError(SelectorParseErrorKind.intoDefaultParserError(.{ .unexpected_selector_after_pseudo_element = next.* })) }; + } break; } @@ -658,6 +705,10 @@ pub const Direction = enum { /// Right to left rtl, + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } + pub fn asStr(this: *const @This()) []const u8 { return css.enum_property_util.asStr(@This(), this); } @@ -678,11 +729,23 @@ pub const PseudoClass = union(enum) { lang: struct { /// A list of language codes. languages: ArrayList([]const u8), + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } + + pub fn __generateHash() void {} }, /// The [:dir()](https://drafts.csswg.org/selectors-4/#the-dir-pseudo) pseudo class. dir: struct { /// A direction. direction: Direction, + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } + + pub fn __generateHash() void {} }, // https://drafts.csswg.org/selectors-4/#useraction-pseudos @@ -799,11 +862,23 @@ pub const PseudoClass = union(enum) { local: struct { /// A local selector. selector: *Selector, + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } + + pub fn __generateHash() void {} }, /// The CSS modules :global() pseudo class. global: struct { /// A global selector. selector: *Selector, + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } + + pub fn __generateHash() void {} }, /// A [webkit scrollbar](https://webkit.org/blog/363/styling-scrollbars/) pseudo class. @@ -813,6 +888,12 @@ pub const PseudoClass = union(enum) { custom: struct { /// The pseudo class name. name: []const u8, + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } + + pub fn __generateHash() void {} }, /// An unknown functional pseudo class. custom_function: struct { @@ -820,8 +901,32 @@ pub const PseudoClass = union(enum) { name: []const u8, /// The arguments of the pseudo class function. arguments: css.TokenList, + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } + + pub fn __generateHash() void {} }, + pub fn isEquivalent(this: *const PseudoClass, other: *const PseudoClass) bool { + if (this.* == .fullscreen and other.* == .fullscreen) return true; + if (this.* == .any_link and other.* == .any_link) return true; + if (this.* == .read_only and other.* == .read_only) return true; + if (this.* == .read_write and other.* == .read_write) return true; + if (this.* == .placeholder_shown and other.* == .placeholder_shown) return true; + if (this.* == .autofill and other.* == .autofill) return true; + return this.eql(other); + } + + pub fn eql(lhs: *const PseudoClass, rhs: *const PseudoClass) bool { + return css.implementEql(PseudoClass, lhs, rhs); + } + + pub fn hash(this: *const @This(), hasher: *std.hash.Wyhash) void { + return css.implementHash(@This(), this, hasher); + } + pub fn toCss(this: *const PseudoClass, comptime W: type, dest: *Printer(W)) PrintErr!void { var s = ArrayList(u8){}; // PERF(alloc): I don't like making these little allocations @@ -833,6 +938,28 @@ pub const PseudoClass = union(enum) { return dest.writeStr(s.items); } + pub fn getPrefix(this: *const PseudoClass) css.VendorPrefix { + return switch (this.*) { + inline .fullscreen, .any_link, .read_only, .read_write, .placeholder_shown, .autofill => |p| p, + else => css.VendorPrefix.empty(), + }; + } + + pub fn getNecessaryPrefixes(this: *PseudoClass, targets: css.targets.Targets) css.VendorPrefix { + const F = css.prefixes.Feature; + const p: *css.VendorPrefix, const feature: F = switch (this.*) { + .fullscreen => |*p| .{ p, F.pseudo_class_fullscreen }, + .any_link => |*p| .{ p, F.pseudo_class_any_link }, + .read_only => |*p| .{ p, F.pseudo_class_read_only }, + .read_write => |*p| .{ p, F.pseudo_class_read_write }, + .placeholder_shown => |*p| .{ p, F.pseudo_class_placeholder_shown }, + .autofill => |*p| .{ p, F.pseudo_class_autofill }, + else => return css.VendorPrefix.empty(), + }; + p.* = targets.prefixes(p.*, feature); + return p.*; + } + pub fn isUserActionState(this: *const PseudoClass) bool { return switch (this.*) { .active, .hover => true, @@ -897,6 +1024,10 @@ pub const WebKitScrollbarPseudoElement = enum { corner, /// ::-webkit-resizer resizer, + + pub inline fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return lhs.* == rhs.*; + } }; pub const SelectorParser = struct { @@ -1300,10 +1431,28 @@ pub fn GenericSelectorList(comptime Impl: type) type { const SelectorT = GenericSelector(Impl); return struct { // PERF: make this equivalent to SmallVec<[Selector; 1]> - v: ArrayList(SelectorT) = .{}, + v: css.SmallList(SelectorT, 1) = .{}, const This = @This(); + pub fn anyHasPseudoElement(this: *const This) bool { + for (this.v.slice()) |*sel| { + if (sel.hasPseudoElement()) return true; + } + return false; + } + + pub fn specifitiesAllEqual(this: *const This) bool { + if (this.v.len() == 0) return true; + if (this.v.len() == 1) return true; + + const value = this.v.at(0).specifity(); + for (this.v.slice()[1..]) |*sel| { + if (sel.specifity() != value) return false; + } + return true; + } + pub fn toCss(this: *const This, comptime W: type, dest: *Printer(W)) PrintErr!void { _ = this; // autofix _ = dest; // autofix @@ -1347,7 +1496,7 @@ pub fn GenericSelectorList(comptime Impl: type) type { ) Result(This) { const original_state = state.*; // TODO: Think about deinitialization in error cases - var values = ArrayList(SelectorT){}; + var values = SmallList(SelectorT, 1){}; while (true) { const Closure = struct { @@ -1376,7 +1525,7 @@ pub fn GenericSelectorList(comptime Impl: type) type { const was_ok = selector.isOk(); switch (selector) { .result => |sel| { - values.append(input.allocator(), sel) catch bun.outOfMemory(); + values.append(input.allocator(), sel); }, .err => |e| { switch (recovery) { @@ -1407,7 +1556,7 @@ pub fn GenericSelectorList(comptime Impl: type) type { ) Result(This) { const original_state = state.*; // TODO: Think about deinitialization in error cases - var values = ArrayList(SelectorT){}; + var values = SmallList(SelectorT, 1){}; while (true) { const Closure = struct { @@ -1436,7 +1585,7 @@ pub fn GenericSelectorList(comptime Impl: type) type { const was_ok = selector.isOk(); switch (selector) { .result => |sel| { - values.append(input.allocator(), sel) catch bun.outOfMemory(); + values.append(input.allocator(), sel); }, .err => |e| { switch (recovery) { @@ -1459,9 +1608,21 @@ pub fn GenericSelectorList(comptime Impl: type) type { pub fn fromSelector(allocator: Allocator, selector: GenericSelector(Impl)) This { var result = This{}; - result.v.append(allocator, selector) catch unreachable; + result.v.append(allocator, selector); return result; } + + pub fn deepClone(this: *const @This(), allocator: Allocator) This { + return .{ .v = this.v.deepClone(allocator) }; + } + + pub fn eql(lhs: *const This, rhs: *const This) bool { + return lhs.v.eql(&rhs.v); + } + + pub fn hash(this: *const @This(), hasher: *std.hash.Wyhash) void { + return css.implementHash(@This(), this, hasher); + } }; } @@ -1489,12 +1650,50 @@ pub fn GenericSelector(comptime Impl: type) type { const This = @This(); + /// Parse a selector, without any pseudo-element. + pub fn parse(parser: *SelectorParser, input: *css.Parser) Result(This) { + var state = SelectorParsingState.empty(); + return parse_selector(Impl, parser, input, &state, .none); + } + pub fn toCss(this: *const This, comptime W: type, dest: *Printer(W)) PrintErr!void { _ = this; // autofix _ = dest; // autofix @compileError("Do not call this! Use `serializer.serializeSelector()` or `tocss_servo.toCss_Selector()` instead."); } + pub fn append(this: *This, allocator: Allocator, component: GenericComponent(Impl)) void { + const index = index: { + for (this.components.items, 0..) |*comp, i| { + switch (comp.*) { + .combinator, .pseudo_element => break :index i, + else => {}, + } + } + break :index this.components.items.len; + }; + this.components.insert(allocator, index, component) catch bun.outOfMemory(); + } + + pub fn deepClone(this: *const @This(), allocator: Allocator) This { + return css.generic.deepClone(@This(), this, allocator); + } + + pub fn eql(this: *const This, other: *const This) bool { + return css.implementEql(This, this, other); + } + + pub fn hasCombinator(this: *const This) bool { + for (this.components.items) |*c| { + if (c.* == .combinator and c.combinator.isTreeCombinator()) return true; + } + return false; + } + + pub fn hasPseudoElement(this: *const This) bool { + return this.specifity_and_flags.hasPseudoElement(); + } + /// Returns count of simple selectors and combinators in the Selector. pub fn len(this: *const This) usize { return this.components.items.len; @@ -1518,12 +1717,6 @@ pub fn GenericSelector(comptime Impl: type) type { return this.specifity_and_flags.specificity; } - /// Parse a selector, without any pseudo-element. - pub fn parse(parser: *SelectorParser, input: *css.Parser) Result(This) { - var state = SelectorParsingState.empty(); - return parse_selector(Impl, parser, input, &state, .none); - } - pub fn parseWithOptions(input: *css.Parser, options: *const css.ParserOptions) Result(This) { var selector_parser = SelectorParser{ .is_nesting_allowed = true, @@ -1552,6 +1745,10 @@ pub fn GenericSelector(comptime Impl: type) type { return result; } }; + + pub fn hash(this: *const @This(), hasher: *std.hash.Wyhash) void { + return css.implementHash(@This(), this, hasher); + } }; } @@ -1571,6 +1768,12 @@ pub fn GenericComponent(comptime Impl: type) type { namespace: struct { prefix: Impl.SelectorImpl.NamespacePrefix, url: Impl.SelectorImpl.NamespaceUrl, + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } + + pub fn __generateHash() void {} }, explicit_universal_type, @@ -1582,6 +1785,11 @@ pub fn GenericComponent(comptime Impl: type) type { attribute_in_no_namespace_exists: struct { local_name: Impl.SelectorImpl.LocalName, local_name_lower: Impl.SelectorImpl.LocalName, + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } + pub fn __generateHash() void {} }, /// Used only when local_name is already lowercase. attribute_in_no_namespace: struct { @@ -1590,6 +1798,11 @@ pub fn GenericComponent(comptime Impl: type) type { value: Impl.SelectorImpl.AttrValue, case_sensitivity: attrs.ParsedCaseSensitivity, never_matches: bool, + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } + pub fn __generateHash() void {} }, /// Use a Box in the less common cases with more data to keep size_of::() small. attribute_other: *attrs.AttrSelectorWithOptionalNamespace(Impl), @@ -1643,6 +1856,11 @@ pub fn GenericComponent(comptime Impl: type) type { any: struct { vendor_prefix: Impl.SelectorImpl.VendorPrefix, selectors: []GenericSelector(Impl), + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } + pub fn __generateHash() void {} }, /// The `:has` pseudo-class. /// @@ -1659,6 +1877,14 @@ pub fn GenericComponent(comptime Impl: type) type { const This = @This(); + pub fn deepClone(this: *const This, allocator: Allocator) *This { + css.implementDeepClone(This, this, allocator); + } + + pub fn eql(lhs: *const This, rhs: *const This) bool { + return css.implementEql(This, lhs, rhs); + } + pub fn format(this: *const This, comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void { switch (this.*) { .local_name => return try writer.print("local_name={s}", .{this.local_name.name.v}), @@ -1701,6 +1927,10 @@ pub fn GenericComponent(comptime Impl: type) type { _ = dest; // autofix @compileError("Do not call this! Use `serializer.serializeComponent()` or `tocss_servo.toCss_Component()` instead."); } + + pub fn hash(this: *const @This(), hasher: *std.hash.Wyhash) void { + return css.implementHash(@This(), this, hasher); + } }; } @@ -1787,6 +2017,14 @@ pub const NthSelectorData = struct { try dest.writeFmt("{}n{s}{d}", .{ this.a, numberSign(this.b), this.b }); } } + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } + + pub fn hash(this: *const @This(), hasher: *std.hash.Wyhash) void { + return css.implementHash(@This(), this, hasher); + } }; /// The properties that comprise an :nth- pseudoclass as of Selectors 4 (e.g., @@ -1797,6 +2035,18 @@ pub fn NthOfSelectorData(comptime Impl: type) type { data: NthSelectorData, selectors: []GenericSelector(Impl), + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } + + pub fn hash(this: *const @This(), hasher: *std.hash.Wyhash) void { + return css.implementHash(@This(), this, hasher); + } + + pub fn deepClone(this: *const @This(), allocator: Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } + pub fn nthData(this: *const @This()) NthSelectorData { return this.data; } @@ -1895,6 +2145,18 @@ pub const SpecifityAndFlags = struct { specificity: u32, /// There's padding after this field due to the size of the flags. flags: SelectorFlags, + + pub fn eql(this: *const SpecifityAndFlags, other: *const SpecifityAndFlags) bool { + return this.specificity == other.specificity and this.flags.eql(other.flags); + } + + pub fn hasPseudoElement(this: *const SpecifityAndFlags) bool { + return this.flags.intersects(SelectorFlags{ .has_pseudo = true }); + } + + pub fn hash(this: *const @This(), hasher: *std.hash.Wyhash) void { + return css.implementHash(@This(), this, hasher); + } }; pub const SelectorFlags = packed struct(u8) { @@ -1953,12 +2215,23 @@ pub const Combinator = enum { /// And still supported as an alias for >>> by Vue. deep, + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return lhs.* == rhs.*; + } + pub fn toCss(this: *const @This(), comptime W: type, dest: *Printer(W)) PrintErr!void { _ = this; // autofix _ = dest; // autofix @compileError("Do not call this! Use `serializer.serializeCombinator()` or `tocss_servo.toCss_Combinator()` instead."); } + pub fn isTreeCombinator(this: *const @This()) bool { + return switch (this.*) { + .child, .descendant, .next_sibling, .later_sibling => true, + else => false, + }; + } + pub fn format(this: *const Combinator, comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void { return switch (this.*) { .child => writer.print(">", .{}), @@ -1977,6 +2250,7 @@ pub const SelectorParseErrorKind = union(enum) { unsupported_pseudo_class_or_element: []const u8, no_qualified_name_in_attribute_selector: css.Token, unexpected_token_in_attribute_selector: css.Token, + unexpected_selector_after_pseudo_element: css.Token, invalid_qual_name_in_attr: css.Token, expected_bar_in_attr: css.Token, empty_selector, @@ -2018,6 +2292,7 @@ pub const SelectorParseErrorKind = union(enum) { .bad_value_in_attr => |token| .{ .bad_value_in_attr = token }, .explicit_namespace_unexpected_token => |token| .{ .explicit_namespace_unexpected_token = token }, .unexpected_ident => |ident| .{ .unexpected_ident = ident }, + .unexpected_selector_after_pseudo_element => |tok| .{ .unexpected_selector_after_pseudo_element = tok }, }; } }; @@ -2064,11 +2339,23 @@ pub const PseudoElement = union(enum) { cue_function: struct { /// The selector argument. selector: *Selector, + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } + + pub fn __generateHash() void {} }, /// The [::cue-region()](https://w3c.github.io/webvtt/#cue-region-selector) functional pseudo element. cue_region_function: struct { /// The selector argument. selector: *Selector, + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } + + pub fn __generateHash() void {} }, /// The [::view-transition](https://w3c.github.io/csswg-drafts/css-view-transitions-1/#view-transition) pseudo element. view_transition, @@ -2076,26 +2363,56 @@ pub const PseudoElement = union(enum) { view_transition_group: struct { /// A part name selector. part_name: ViewTransitionPartName, + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } + + pub fn __generateHash() void {} }, /// The [::view-transition-image-pair()](https://w3c.github.io/csswg-drafts/css-view-transitions-1/#view-transition-image-pair-pt-name-selector) functional pseudo element. view_transition_image_pair: struct { /// A part name selector. part_name: ViewTransitionPartName, + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } + + pub fn __generateHash() void {} }, /// The [::view-transition-old()](https://w3c.github.io/csswg-drafts/css-view-transitions-1/#view-transition-old-pt-name-selector) functional pseudo element. view_transition_old: struct { /// A part name selector. part_name: ViewTransitionPartName, + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } + + pub fn __generateHash() void {} }, /// The [::view-transition-new()](https://w3c.github.io/csswg-drafts/css-view-transitions-1/#view-transition-new-pt-name-selector) functional pseudo element. view_transition_new: struct { /// A part name selector. part_name: ViewTransitionPartName, + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } + + pub fn __generateHash() void {} }, /// An unknown pseudo element. custom: struct { /// The name of the pseudo element. name: []const u8, + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } + + pub fn __generateHash() void {} }, /// An unknown functional pseudo element. custom_function: struct { @@ -2103,8 +2420,52 @@ pub const PseudoElement = union(enum) { name: []const u8, /// The arguments of the pseudo element function. arguments: css.TokenList, + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } + + pub fn __generateHash() void {} }, + pub fn isEquivalent(this: *const PseudoElement, other: *const PseudoElement) bool { + if (this.* == .selection and other.* == .selection) return true; + if (this.* == .placeholder and other.* == .placeholder) return true; + if (this.* == .backdrop and other.* == .backdrop) return true; + if (this.* == .file_selector_button and other.* == .file_selector_button) return true; + return this.eql(other); + } + + pub fn eql(this: *const PseudoElement, other: *const PseudoElement) bool { + return css.implementEql(PseudoElement, this, other); + } + + pub fn hash(this: *const @This(), hasher: *std.hash.Wyhash) void { + return css.implementHash(@This(), this, hasher); + } + + pub fn getNecessaryPrefixes(this: *PseudoElement, targets: css.targets.Targets) css.VendorPrefix { + const F = css.prefixes.Feature; + const p: *css.VendorPrefix, const feature: F = switch (this.*) { + .selection => |*p| .{ p, F.pseudo_element_selection }, + .placeholder => |*p| .{ p, F.pseudo_element_placeholder }, + .backdrop => |*p| .{ p, F.pseudo_element_backdrop }, + .file_selector_button => |*p| .{ p, F.pseudo_element_file_selector_button }, + else => return css.VendorPrefix.empty(), + }; + + p.* = targets.prefixes(p.*, feature); + + return p.*; + } + + pub fn getPrefix(this: *const PseudoElement) css.VendorPrefix { + return switch (this.*) { + .selection, .placeholder, .backdrop, .file_selector_button => |p| p, + else => css.VendorPrefix.empty(), + }; + } + pub fn format(this: *const PseudoElement, comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void { try writer.print("{s}", .{@tagName(this.*)}); } @@ -2882,7 +3243,7 @@ pub fn parse_nth_pseudo_class( return .{ .result = .{ .nth_of = NthOfSelectorData(Impl){ .data = nth_data, - .selectors = selectors.v.items, + .selectors = selectors.v.toOwnedSlice(input.allocator()), }, } }; } @@ -2917,7 +3278,7 @@ pub fn parse_is_or_where( state.after_nesting = true; } - const selector_slice = inner.v.items; + const selector_slice = inner.v.toOwnedSlice(input.allocator()); const result = result: { const args = brk: { @@ -2958,7 +3319,7 @@ pub fn parse_has( if (child_state.after_nesting) { state.after_nesting = true; } - return .{ .result = .{ .has = inner.v.items } }; + return .{ .result = .{ .has = inner.v.toOwnedSlice(input.allocator()) } }; } /// Level 3: Parse **one** simple_selector. (Though we might insert a second @@ -2982,7 +3343,7 @@ pub fn parse_negation( state.after_nesting = true; } - return .{ .result = .{ .negation = list.v.items } }; + return .{ .result = .{ .negation = list.v.toOwnedSlice(input.allocator()) } }; } pub fn OptionalQName(comptime Impl: type) type { @@ -3132,6 +3493,12 @@ pub fn LocalName(comptime Impl: type) type { pub fn toCss(this: *const @This(), comptime W: type, dest: *Printer(W)) PrintErr!void { return css.IdentFns.toCss(&this.name, W, dest); } + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } + + pub fn __generateHash() void {} }; } @@ -3213,6 +3580,14 @@ pub const ViewTransitionPartName = union(enum) { /// name: css.css_values.ident.CustomIdent, + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } + + pub fn hash(this: *const @This(), hasher: *std.hash.Wyhash) void { + return css.implementHash(@This(), this, hasher); + } + pub fn toCss(this: *const @This(), comptime W: type, dest: *css.Printer(W)) css.PrintErr!void { return switch (this.*) { .all => try dest.writeStr("*"), diff --git a/src/css/selectors/selector.zig b/src/css/selectors/selector.zig index ec47030599..64c9eecf34 100644 --- a/src/css/selectors/selector.zig +++ b/src/css/selectors/selector.zig @@ -3,7 +3,6 @@ const Allocator = std.mem.Allocator; const bun = @import("root").bun; const logger = bun.logger; const Log = logger.Log; -const debug = bun.Output.scoped(.css, true); pub const css = @import("../css_parser.zig"); const CSSString = css.CSSString; @@ -17,6 +16,14 @@ const PrintResult = css.PrintResult; const ArrayList = std.ArrayListUnmanaged; +pub const Selector = parser.Selector; +pub const SelectorList = parser.SelectorList; +pub const Component = parser.Component; +pub const PseudoClass = parser.PseudoClass; +pub const PseudoElement = parser.PseudoElement; + +const debug = bun.Output.scoped(.CSS_SELECTORS, false); + /// Our implementation of the `SelectorImpl` interface /// pub const impl = struct { @@ -40,6 +47,430 @@ pub const impl = struct { pub const parser = @import("./parser.zig"); +/// Returns whether two selector lists are equivalent, i.e. the same minus any vendor prefix differences. +pub fn isEquivalent(selectors: []const Selector, other: []const Selector) bool { + if (selectors.len != other.len) return false; + + for (selectors, 0..) |*a, i| { + const b = &other[i]; + if (a.len() != b.len()) return false; + + for (a.components.items, b.components.items) |*a_comp, *b_comp| { + const is_equivalent = blk: { + if (a_comp.* == .non_ts_pseudo_class and b_comp.* == .non_ts_pseudo_class) { + break :blk a_comp.non_ts_pseudo_class.isEquivalent(&b_comp.non_ts_pseudo_class); + } else if (a_comp.* == .pseudo_element and b_comp.* == .pseudo_element) { + break :blk a_comp.pseudo_element.isEquivalent(&b_comp.pseudo_element); + } else if ((a_comp.* == .any and b_comp.* == .is) or + (a_comp.* == .is and b_comp.* == .any) or + (a_comp.* == .any and b_comp.* == .any) or + (a_comp.* == .is and b_comp.* == .is)) + { + const a_selectors = switch (a_comp.*) { + .any => |v| v.selectors, + .is => |v| v, + else => unreachable, + }; + const b_selectors = switch (b_comp.*) { + .any => |v| v.selectors, + .is => |v| v, + else => unreachable, + }; + break :blk isEquivalent(a_selectors, b_selectors); + } else { + break :blk Component.eql(a_comp, b_comp); + } + }; + + if (!is_equivalent) { + return false; + } + } + } + + return true; +} + +/// Downlevels the given selectors to be compatible with the given browser targets. +/// Returns the necessary vendor prefixes. +pub fn downlevelSelectors(allocator: Allocator, selectors: []Selector, targets: css.targets.Targets) css.VendorPrefix { + var necessary_prefixes = css.VendorPrefix.empty(); + for (selectors) |*selector| { + for (selector.components.items) |*component| { + necessary_prefixes.insert(downlevelComponent(allocator, component, targets)); + } + } + return necessary_prefixes; +} + +pub fn downlevelComponent(allocator: Allocator, component: *Component, targets: css.targets.Targets) css.VendorPrefix { + return switch (component.*) { + .non_ts_pseudo_class => |*pc| { + return switch (pc.*) { + .dir => |*d| { + if (targets.shouldCompileSame(.dir_selector)) { + component.* = downlevelDir(allocator, d.direction, targets); + return downlevelComponent(allocator, component, targets); + } + return css.VendorPrefix.empty(); + }, + .lang => |l| { + // :lang() with multiple languages is not supported everywhere. + // compile this to :is(:lang(a), :lang(b)) etc. + if (l.languages.items.len > 1 and targets.shouldCompileSame(.lang_selector_list)) { + component.* = .{ .is = langListToSelectors(allocator, l.languages.items) }; + return downlevelComponent(allocator, component, targets); + } + return css.VendorPrefix.empty(); + }, + else => pc.getNecessaryPrefixes(targets), + }; + }, + .pseudo_element => |*pe| pe.getNecessaryPrefixes(targets), + .is => |selectors| { + var necessary_prefixes = downlevelSelectors(allocator, selectors, targets); + + // Convert :is to :-webkit-any/:-moz-any if needed. + // All selectors must be simple, no combinators are supported. + if (targets.shouldCompileSame(.is_selector) and + !shouldUnwrapIs(selectors) and brk: { + for (selectors) |*selector| { + if (selector.hasCombinator()) break :brk false; + } + break :brk true; + }) { + necessary_prefixes.insert(targets.prefixes(css.VendorPrefix{ .none = true }, .any_pseudo)); + } else { + necessary_prefixes.insert(css.VendorPrefix{ .none = true }); + } + + return necessary_prefixes; + }, + .negation => |selectors| { + var necessary_prefixes = downlevelSelectors(allocator, selectors, targets); + + // Downlevel :not(.a, .b) -> :not(:is(.a, .b)) if not list is unsupported. + // We need to use :is() / :-webkit-any() rather than :not(.a):not(.b) to ensure the specificity is equivalent. + // https://drafts.csswg.org/selectors/#specificity-rules + if (selectors.len == 1 and css.targets.Targets.shouldCompileSame(&targets, .not_selector_list)) { + const is: Selector = Selector.fromComponent(allocator, Component{ .is = selectors }); + var list = ArrayList(Selector).initCapacity(allocator, 1) catch bun.outOfMemory(); + list.appendAssumeCapacity(is); + component.* = .{ .negation = list.items }; + + if (targets.shouldCompileSame(.is_selector)) { + necessary_prefixes.insert(targets.prefixes(css.VendorPrefix{ .none = true }, .any_pseudo)); + } else { + necessary_prefixes.insert(css.VendorPrefix{ .none = true }); + } + } + + return necessary_prefixes; + }, + .where, .has => |s| downlevelSelectors(allocator, s, targets), + .any => |*a| downlevelSelectors(allocator, a.selectors, targets), + else => css.VendorPrefix.empty(), + }; +} + +const RTL_LANGS: []const []const u8 = &.{ + "ae", "ar", "arc", "bcc", "bqi", "ckb", "dv", "fa", "glk", "he", "ku", "mzn", "nqo", "pnb", "ps", "sd", "ug", + "ur", "yi", +}; + +fn downlevelDir(allocator: Allocator, dir: parser.Direction, targets: css.targets.Targets) Component { + // Convert :dir to :lang. If supported, use a list of languages in a single :lang, + // otherwise, use :is/:not, which may be further downleveled to e.g. :-webkit-any. + if (targets.shouldCompileSame(.lang_selector_list)) { + const c = Component{ + .non_ts_pseudo_class = PseudoClass{ + .lang = .{ .languages = lang: { + var list = ArrayList([]const u8).initCapacity(allocator, RTL_LANGS.len) catch bun.outOfMemory(); + list.appendSliceAssumeCapacity(RTL_LANGS); + break :lang list; + } }, + }, + }; + if (dir == .ltr) return Component{ + .negation = negation: { + var list = allocator.alloc(Selector, 1) catch bun.outOfMemory(); + list[0] = Selector.fromComponent(allocator, c); + break :negation list; + }, + }; + return c; + } else { + if (dir == .ltr) return Component{ .negation = langListToSelectors(allocator, RTL_LANGS) }; + return Component{ .is = langListToSelectors(allocator, RTL_LANGS) }; + } +} + +fn langListToSelectors(allocator: Allocator, langs: []const []const u8) []Selector { + var selectors = allocator.alloc(Selector, langs.len) catch bun.outOfMemory(); + for (langs, selectors[0..]) |lang, *sel| { + sel.* = Selector.fromComponent(allocator, Component{ + .non_ts_pseudo_class = PseudoClass{ + .lang = .{ .languages = langs: { + var list = ArrayList([]const u8).initCapacity(allocator, 1) catch bun.outOfMemory(); + list.appendAssumeCapacity(lang); + break :langs list; + } }, + }, + }); + } + return selectors; +} + +/// Returns the vendor prefix (if any) used in the given selector list. +/// If multiple vendor prefixes are seen, this is invalid, and an empty result is returned. +pub fn getPrefix(selectors: *const SelectorList) css.VendorPrefix { + var prefix = css.VendorPrefix.empty(); + for (selectors.v.slice()) |*selector| { + for (selector.components.items) |*component_| { + const component: *const Component = component_; + const p = switch (component.*) { + // Return none rather than empty for these so that we call downlevel_selectors. + .non_ts_pseudo_class => |*pc| switch (pc.*) { + .lang => css.VendorPrefix{ .none = true }, + .dir => css.VendorPrefix{ .none = true }, + else => pc.getPrefix(), + }, + .is => css.VendorPrefix{ .none = true }, + .where => css.VendorPrefix{ .none = true }, + .has => css.VendorPrefix{ .none = true }, + .negation => css.VendorPrefix{ .none = true }, + .any => |*any| any.vendor_prefix, + .pseudo_element => |*pe| pe.getPrefix(), + else => css.VendorPrefix.empty(), + }; + + if (!p.isEmpty()) { + // Allow none to be mixed with a prefix. + const prefix_without_none = prefix.maskOut(css.VendorPrefix{ .none = true }); + if (prefix_without_none.isEmpty() or prefix_without_none.eql(p)) { + prefix.insert(p); + } else { + return css.VendorPrefix.empty(); + } + } + } + } + + return prefix; +} + +pub fn isCompatible(selectors: []const parser.Selector, targets: css.targets.Targets) bool { + const F = css.compat.Feature; + for (selectors) |*selector| { + for (selector.components.items) |*component| { + const feature = switch (component.*) { + .id, .class, .local_name => continue, + + .explicit_any_namespace, + .explicit_no_namespace, + .default_namespace, + .namespace, + => F.namespaces, + + .explicit_universal_type => F.selectors2, + + .attribute_in_no_namespace_exists => F.selectors2, + + .attribute_in_no_namespace => |x| brk: { + if (x.case_sensitivity != parser.attrs.ParsedCaseSensitivity.case_sensitive) break :brk F.case_insensitive; + break :brk switch (x.operator) { + .equal, .includes, .dash_match => F.selectors2, + .prefix, .substring, .suffix => F.selectors3, + }; + }, + + .attribute_other => |attr| switch (attr.operation) { + .exists => F.selectors2, + .with_value => |*x| brk: { + if (x.case_sensitivity != parser.attrs.ParsedCaseSensitivity.case_sensitive) break :brk F.case_insensitive; + + break :brk switch (x.operator) { + .equal, .includes, .dash_match => F.selectors2, + .prefix, .substring, .suffix => F.selectors3, + }; + }, + }, + + .empty, .root => F.selectors3, + .negation => |sels| { + // :not() selector list is not forgiving. + if (!targets.isCompatible(F.selectors3) or !isCompatible(sels, targets)) return false; + continue; + }, + + .nth => |*data| brk: { + if (data.ty == .child and data.a == 0 and data.b == 1) break :brk F.selectors2; + if (data.ty == .col or data.ty == .last_col) return false; + break :brk F.selectors3; + }, + .nth_of => |*n| { + if (!targets.isCompatible(F.nth_child_of) or !isCompatible(n.selectors, targets)) return false; + continue; + }, + + // These support forgiving selector lists, so no need to check nested selectors. + .is => |sels| brk: { + // ... except if we are going to unwrap them. + if (shouldUnwrapIs(sels) and isCompatible(sels, targets)) continue; + break :brk F.is_selector; + }, + .where, .nesting => F.is_selector, + .any => return false, + .has => |sels| { + if (!targets.isCompatible(F.has_selector) or !isCompatible(sels, targets)) return false; + continue; + }, + + .scope, .host, .slotted => F.shadowdomv1, + + .part => F.part_pseudo, + + .non_ts_pseudo_class => |*pseudo| brk: { + switch (pseudo.*) { + .link, .visited, .active, .hover, .focus, .lang => break :brk F.selectors2, + + .checked, .disabled, .enabled, .target => break :brk F.selectors3, + + .any_link => |prefix| { + if (prefix.eql(css.VendorPrefix{ .none = true })) break :brk F.any_link; + }, + .indeterminate => break :brk F.indeterminate_pseudo, + + .fullscreen => |prefix| { + if (prefix.eql(css.VendorPrefix{ .none = true })) break :brk F.fullscreen; + }, + + .focus_visible => break :brk F.focus_visible, + .focus_within => break :brk F.focus_within, + .default => break :brk F.default_pseudo, + .dir => break :brk F.dir_selector, + .optional => break :brk F.optional_pseudo, + .placeholder_shown => |prefix| { + if (prefix.eql(css.VendorPrefix{ .none = true })) break :brk F.placeholder_shown; + }, + + inline .read_only, .read_write => |prefix| { + if (prefix.eql(css.VendorPrefix{ .none = true })) break :brk F.read_only_write; + }, + + .valid, .invalid, .required => break :brk F.form_validation, + .in_range, .out_of_range => break :brk F.in_out_of_range, + + .autofill => |prefix| { + if (prefix.eql(css.VendorPrefix{ .none = true })) break :brk F.autofill; + }, + + // Experimental, no browser support. + .current, + .past, + .future, + .playing, + .paused, + .seeking, + .stalled, + .buffering, + .muted, + .volume_locked, + .target_within, + .local_link, + .blank, + .user_invalid, + .user_valid, + .defined, + => return false, + + .custom => {}, + + else => {}, + } + return false; + }, + + .pseudo_element => |*pseudo| brk: { + switch (pseudo.*) { + .after, .before => break :brk F.gencontent, + .first_line => break :brk F.first_line, + .first_letter => break :brk F.first_letter, + .selection => |prefix| { + if (prefix.eql(css.VendorPrefix{ .none = true })) break :brk F.selection; + }, + .placeholder => |prefix| { + if (prefix.eql(css.VendorPrefix{ .none = true })) break :brk F.placeholder; + }, + .marker => break :brk F.marker_pseudo, + .backdrop => |prefix| { + if (prefix.eql(css.VendorPrefix{ .none = true })) break :brk F.dialog; + }, + .cue => break :brk F.cue, + .cue_function => break :brk F.cue_function, + .custom => return false, + else => {}, + } + return false; + }, + + .combinator => |*combinator| brk: { + break :brk switch (combinator.*) { + .child, .next_sibling => F.selectors2, + .later_sibling => F.selectors3, + else => continue, + }; + }, + }; + + if (!targets.isCompatible(feature)) return false; + } + } + + return true; +} + +/// Determines whether a selector list contains only unused selectors. +/// A selector is considered unused if it contains a class or id component that exists in the set of unused symbols. +pub fn isUnused( + selectors: []const parser.Selector, + unused_symbols: *const std.StringArrayHashMapUnmanaged(void), + parent_is_unused: bool, +) bool { + if (unused_symbols.count() == 0) return false; + + for (selectors) |*selector| { + if (!isSelectorUnused(selector, unused_symbols, parent_is_unused)) return false; + } + + return true; +} + +fn isSelectorUnused( + selector: *const parser.Selector, + unused_symbols: *const std.StringArrayHashMapUnmanaged(void), + parent_is_unused: bool, +) bool { + for (selector.components.items) |*component| { + switch (component.*) { + .class, .id => |ident| { + if (unused_symbols.contains(ident.v)) return true; + }, + .is, .where => |is| { + if (isUnused(is, unused_symbols, parent_is_unused)) return true; + }, + .any => |any| { + if (isUnused(any.selectors, unused_symbols, parent_is_unused)) return true; + }, + .nesting => { + if (parent_is_unused) return true; + }, + else => {}, + } + } + return false; +} + /// The serialization module ported from lightningcss. /// /// Note that we have two serialization modules, one from lightningcss and one from servo. @@ -73,18 +504,19 @@ pub const serialize = struct { var is_relative = __is_relative; if (comptime bun.Environment.isDebug) { - debug("Selector components:", .{}); + debug("Selector components:\n", .{}); for (selector.components.items) |*comp| { debug(" {}\n", .{comp}); } - debug("Compound selector iters", .{}); + debug("Compound selector iter\n", .{}); var compound_selectors = CompoundSelectorIter{ .sel = selector }; while (compound_selectors.next()) |comp| { for (comp) |c| { debug(" {}, ", .{c}); } } + debug("\n", .{}); } // Compound selectors invert the order of their contents, so we need to @@ -724,14 +1156,14 @@ pub const serialize = struct { // Otherwise, use an :is() pseudo class. // Type selectors are only allowed at the start of a compound selector, // so use :is() if that is not the case. - if (ctx.selectors.v.items.len == 1 and - (first or (!hasTypeSelector(&ctx.selectors.v.items[0]) and - isSimple(&ctx.selectors.v.items[0])))) + if (ctx.selectors.v.len() == 1 and + (first or (!hasTypeSelector(ctx.selectors.v.at(0)) and + isSimple(ctx.selectors.v.at(0))))) { - try serializeSelector(&ctx.selectors.v.items[0], W, dest, ctx.parent, false); + try serializeSelector(ctx.selectors.v.at(0), W, dest, ctx.parent, false); } else { try dest.writeStr(":is("); - try serializeSelectorList(ctx.selectors.v.items, W, dest, ctx.parent, false); + try serializeSelectorList(ctx.selectors.v.slice(), W, dest, ctx.parent, false); try dest.writeChar(')'); } } else { diff --git a/src/css/small_list.zig b/src/css/small_list.zig new file mode 100644 index 0000000000..ccb64d4f77 --- /dev/null +++ b/src/css/small_list.zig @@ -0,0 +1,363 @@ +const std = @import("std"); +const bun = @import("root").bun; +const css = @import("./css_parser.zig"); +const Printer = css.Printer; +const Parser = css.Parser; +const Result = css.Result; +const voidWrap = css.voidWrap; +const generic = css.generic; +const Delimiters = css.Delimiters; +const PrintErr = css.PrintErr; +const Allocator = std.mem.Allocator; +const implementEql = css.implementEql; + +/// This is a type whose items can either be heap-allocated (essentially the +/// same as a BabyList(T)) or inlined in the struct itself. +/// +/// This is type is a performance optimizations for avoiding allocations, especially when you know the list +/// will commonly have N or fewer items. +/// +/// The `capacity` field is used to disambiguate between the two states: - When +/// `capacity <= N`, the items are stored inline, and `capacity` is the length +/// of the items. - When `capacity > N`, the items are stored on the heap, and +/// this type essentially becomes a BabyList(T), but with the fields reordered. +/// +/// This code is based on servo/rust-smallvec and the Zig std.ArrayList source. +pub fn SmallList(comptime T: type, comptime N: comptime_int) type { + return struct { + capacity: u32 = 0, + data: Data = .{ .inlined = undefined }, + + const Data = union { + inlined: [N]T, + heap: HeapData, + }; + + const HeapData = struct { + len: u32, + ptr: [*]T, + + pub fn initCapacity(allocator: Allocator, capacity: u32) HeapData { + return .{ + .len = 0, + .ptr = (allocator.alloc(T, capacity) catch bun.outOfMemory()).ptr, + }; + } + }; + + const This = @This(); + + pub fn parse(input: *Parser) Result(@This()) { + const parseFn = comptime voidWrap(T, generic.parseFor(T)); + var values: @This() = .{}; + while (true) { + input.skipWhitespace(); + switch (input.parseUntilBefore(Delimiters{ .comma = true }, T, {}, parseFn)) { + .result => |v| { + values.append(input.allocator(), v); + }, + .err => |e| return .{ .err = e }, + } + switch (input.next()) { + .err => return .{ .result = values }, + .result => |t| { + if (t.* == .comma) continue; + std.debug.panic("Expected a comma", .{}); + }, + } + } + unreachable; + } + + pub fn toCss(this: *const @This(), comptime W: type, dest: *Printer(W)) PrintErr!void { + const length = this.len(); + for (this.slice(), 0..) |*val, idx| { + try val.toCss(W, dest); + if (idx < length - 1) { + try dest.delim(',', false); + } + } + } + + pub fn withOne(val: T) @This() { + var ret = This{}; + ret.capacity = 1; + ret.data.inlined[0] = val; + return ret; + } + + pub inline fn at(this: *const @This(), idx: u32) *const T { + return &this.as_const_ptr()[idx]; + } + + pub inline fn mut(this: *@This(), idx: u32) *T { + return &this.as_ptr()[idx]; + } + + pub inline fn toOwnedSlice(this: *const @This(), allocator: Allocator) []T { + if (this.spilled()) return this.data.heap.ptr[0..this.data.heap.len]; + return allocator.dupe(T, this.data.inlined[0..this.capacity]) catch bun.outOfMemory(); + } + + /// NOTE: If this is inlined then this will refer to stack memory, if + /// need it to be stable then you should use `.toOwnedSlice()` + pub inline fn slice(this: *const @This()) []const T { + if (this.capacity > N) return this.data.heap.ptr[0..this.data.heap.len]; + return this.data.inlined[0..this.capacity]; + } + + /// NOTE: If this is inlined then this will refer to stack memory, if + /// need it to be stable then you should use `.toOwnedSlice()` + pub inline fn slice_mut(this: *@This()) []T { + if (this.capacity > N) return this.data.heap.ptr[0..this.data.heap.len]; + return this.data.inlined[0..this.capacity]; + } + + pub fn orderedRemove(this: *@This(), idx: u32) T { + var ptr, const len_ptr, const capp = this.tripleMut(); + _ = capp; // autofix + bun.assert(idx < len_ptr.*); + + const length = len_ptr.*; + + len_ptr.* = len_ptr.* - 1; + ptr += idx; + const item = ptr[0]; + std.mem.copyForwards(T, ptr[0 .. length - idx - 1], ptr[1..][0 .. length - idx - 1]); + + return item; + } + + pub fn swapRemove(this: *@This(), idx: u32) T { + var ptr, const len_ptr, const capp = this.tripleMut(); + _ = capp; // autofix + bun.assert(idx < len_ptr.*); + + const ret = ptr[idx]; + ptr[idx] = ptr[len_ptr.* -| 1]; + len_ptr.* = len_ptr.* - 1; + + return ret; + } + + pub fn clearRetainingCapacity(this: *@This()) void { + if (this.spilled()) { + this.data.heap.len = 0; + } else { + this.capacity = 0; + } + } + + pub fn deepClone(this: *const @This(), allocator: Allocator) @This() { + var ret: @This() = .{}; + ret.appendSlice(allocator, this.slice()); + for (ret.slice_mut()) |*item| { + item.* = generic.deepClone(T, item, allocator); + } + return ret; + } + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + if (lhs.len() != rhs.len()) return false; + for (lhs.slice(), rhs.slice()) |*a, *b| { + if (!generic.eql(T, a, b)) return false; + } + return true; + } + + /// Shallow clone + pub fn clone(this: *const @This(), allocator: Allocator) @This() { + var ret = this.*; + if (!this.spilled()) return ret; + ret.data.heap.ptr = (allocator.dupe(T, ret.data.heap.ptr[0..ret.data.heap.len]) catch bun.outOfMemory()).ptr; + return ret; + } + + pub fn deinit(this: *@This(), allocator: Allocator) void { + if (this.spilled()) { + allocator.free(this.data.heap.ptr[0..this.data.heap.len]); + } + } + + pub fn hash(this: *const @This(), hasher: anytype) void { + for (this.slice()) |*item| { + css.generic.hash(T, item, hasher); + } + } + + pub inline fn len(this: *const @This()) u32 { + if (this.spilled()) return this.data.heap.len; + return this.capacity; + } + + pub inline fn isEmpty(this: *const @This()) bool { + return this.len() == 0; + } + + pub fn initCapacity(allocator: Allocator, capacity: u32) @This() { + if (capacity > N) { + var list: This = .{}; + list.capacity = capacity; + list.data = .{ .heap = HeapData.initCapacity(allocator, capacity) }; + return list; + } + + return .{ + .capacity = 0, + }; + } + + pub fn insert( + this: *@This(), + allocator: Allocator, + index: u32, + item: T, + ) void { + var ptr, var len_ptr, const capp = this.tripleMut(); + if (len_ptr.* == capp) { + this.reserveOneUnchecked(allocator); + const heap_ptr, const heap_len_ptr = this.heap(); + ptr = heap_ptr; + len_ptr = heap_len_ptr; + } + const length = len_ptr.*; + ptr += index; + if (index < length) { + const count = length - index; + std.mem.copyBackwards(T, ptr[1..][0..count], ptr[0..count]); + } else if (index == length) { + // No elements need shifting. + } else { + @panic("index exceeds length"); + } + len_ptr.* = length + 1; + ptr[0] = item; + } + + pub fn append(this: *@This(), allocator: Allocator, item: T) void { + var ptr, var len_ptr, const capp = this.tripleMut(); + if (len_ptr.* == capp) { + this.reserveOneUnchecked(allocator); + const heap_ptr, const heap_len = this.heap(); + ptr = heap_ptr; + len_ptr = heap_len; + } + ptr[len_ptr.*] = item; + len_ptr.* += 1; + } + + pub fn appendSlice(this: *@This(), allocator: Allocator, items: []const T) void { + this.insertSlice(allocator, this.len(), items); + } + + pub fn insertSlice(this: *@This(), allocator: Allocator, index: u32, items: []const T) void { + this.reserve(allocator, @intCast(items.len)); + + const length = this.len(); + bun.assert(index <= length); + const ptr: [*]T = this.as_ptr()[index..]; + const count = length - index; + std.mem.copyBackwards(T, ptr[items.len..][0..count], ptr[0..count]); + @memcpy(ptr[0..items.len], items); + this.setLen(length + @as(u32, @intCast(items.len))); + } + + pub fn setLen(this: *@This(), new_len: u32) void { + const len_ptr = this.lenMut(); + len_ptr.* = new_len; + } + + inline fn heap(this: *@This()) struct { [*]T, *u32 } { + return .{ this.data.heap.ptr, &this.data.heap.len }; + } + + fn as_const_ptr(this: *const @This()) [*]const T { + if (this.spilled()) return this.data.heap.ptr; + return &this.data.inlined; + } + + fn as_ptr(this: *@This()) [*]T { + if (this.spilled()) return this.data.heap.ptr; + return &this.data.inlined; + } + + fn reserve(this: *@This(), allocator: Allocator, additional: u32) void { + const ptr, const __len, const capp = this.tripleMut(); + _ = ptr; // autofix + const len_ = __len.*; + + if (capp - len_ >= additional) return; + const new_cap = growCapacity(capp, len_ + additional); + this.tryGrow(allocator, new_cap); + } + + fn reserveOneUnchecked(this: *@This(), allocator: Allocator) void { + @setCold(true); + bun.assert(this.len() == this.capacity); + const new_cap = growCapacity(this.capacity, this.len() + 1); + this.tryGrow(allocator, new_cap); + } + + fn tryGrow(this: *@This(), allocator: Allocator, new_cap: u32) void { + const unspilled = !this.spilled(); + const ptr, const __len, const cap = this.tripleMut(); + const length = __len.*; + bun.assert(new_cap >= length); + if (new_cap <= N) { + if (unspilled) return; + this.data = .{ .inlined = undefined }; + @memcpy(ptr[0..length], this.data.inlined[0..length]); + this.capacity = length; + allocator.free(ptr[0..length]); + } else if (new_cap != cap) { + const new_alloc: [*]T = if (unspilled) new_alloc: { + const new_alloc = allocator.alloc(T, new_cap) catch bun.outOfMemory(); + @memcpy(new_alloc[0..length], ptr[0..length]); + break :new_alloc new_alloc.ptr; + } else new_alloc: { + break :new_alloc (allocator.realloc(ptr[0..length], new_cap * @sizeOf(T)) catch bun.outOfMemory()).ptr; + }; + this.data = .{ .heap = .{ .ptr = new_alloc, .len = length } }; + this.capacity = new_cap; + } + } + + /// Returns a tuple with (data ptr, len, capacity) + /// Useful to get all SmallVec properties with a single check of the current storage variant. + inline fn tripleMut(this: *@This()) struct { [*]T, *u32, u32 } { + if (this.spilled()) return .{ this.data.heap.ptr, &this.data.heap.len, this.capacity }; + return .{ &this.data.inlined, &this.capacity, N }; + } + + inline fn lenMut(this: *@This()) *u32 { + if (this.spilled()) return &this.data.heap.len; + return &this.capacity; + } + + fn growToHeap(this: *@This(), allocator: Allocator, additional: usize) void { + bun.assert(!this.spilled()); + const new_size = growCapacity(this.capacity, this.capacity + additional); + var slc = allocator.alloc(T, new_size) catch bun.outOfMemory(); + @memcpy(slc[0..this.capacity], this.data.inlined[0..this.capacity]); + this.data = .{ .heap = HeapData{ .len = this.capacity, .ptr = slc.ptr } }; + this.capacity = new_size; + } + + inline fn spilled(this: *const @This()) bool { + return this.capacity > N; + } + + /// Copy pasted from Zig std in array list: + /// + /// Called when memory growth is necessary. Returns a capacity larger than + /// minimum that grows super-linearly. + fn growCapacity(current: u32, minimum: u32) u32 { + var new = current; + while (true) { + new +|= new / 2 + 8; + if (new >= minimum) + return new; + } + } + }; +} diff --git a/src/css/targets.zig b/src/css/targets.zig index b0d7bd5c4d..5da011834b 100644 --- a/src/css/targets.zig +++ b/src/css/targets.zig @@ -19,7 +19,7 @@ pub const Targets = struct { pub fn prefixes(this: *const Targets, prefix: css.VendorPrefix, feature: css.prefixes.Feature) css.VendorPrefix { if (prefix.contains(css.VendorPrefix{ .none = true }) and !this.exclude.contains(css.targets.Features{ .vendor_prefixes = true })) { - if (this.includes(css.targets.Features{ .vendor_prefixes = true })) { + if (this.include.contains(css.targets.Features{ .vendor_prefixes = true })) { return css.VendorPrefix.all(); } else { return if (this.browsers) |b| feature.prefixesFor(b) else prefix; @@ -44,6 +44,11 @@ pub const Targets = struct { return shouldCompile(this, compat_feature, target_feature); } + pub fn shouldCompileSelectors(this: *const Targets) bool { + return this.include.intersects(Features.selectors) or + (!this.exclude.intersects(Features.selectors) and this.browsers != null); + } + pub fn isCompatible(this: *const Targets, feature: css.compat.Feature) bool { if (this.browsers) |*targets| { return feature.isCompatible(targets.*); diff --git a/src/css/values/alpha.zig b/src/css/values/alpha.zig index fae5071776..531e718b52 100644 --- a/src/css/values/alpha.zig +++ b/src/css/values/alpha.zig @@ -34,7 +34,10 @@ pub const AlphaValue = struct { pub fn parse(input: *css.Parser) Result(AlphaValue) { // For some reason NumberOrPercentage.parse makes zls crash, using this instead. - const val: NumberOrPercentage = @call(.auto, @field(NumberOrPercentage, "parse"), .{input}); + const val: NumberOrPercentage = switch (@call(.auto, @field(NumberOrPercentage, "parse"), .{input})) { + .result => |v| v, + .err => |e| return .{ .err = e }, + }; const final = switch (val) { .percentage => |percent| AlphaValue{ .v = percent.v }, .number => |num| AlphaValue{ .v = num }, @@ -45,4 +48,16 @@ pub const AlphaValue = struct { pub fn toCss(this: *const AlphaValue, comptime W: type, dest: *css.Printer(W)) css.PrintErr!void { return CSSNumberFns.toCss(&this.v, W, dest); } + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } + + pub fn hash(this: *const @This(), hasher: *std.hash.Wyhash) void { + return css.implementHash(@This(), this, hasher); + } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } }; diff --git a/src/css/values/angle.zig b/src/css/values/angle.zig index 7c9ea9e5f6..0a50571c85 100644 --- a/src/css/values/angle.zig +++ b/src/css/values/angle.zig @@ -192,6 +192,10 @@ pub const Angle = union(Tag) { return Angle.op(&this, &rhs, {}, addfn.add); } + pub fn tryAdd(this: *const Angle, _: std.mem.Allocator, rhs: *const Angle) ?Angle { + return .{ .deg = this.toDegrees() + rhs.toDegrees() }; + } + pub fn eql(lhs: *const Angle, rhs: *const Angle) bool { return lhs.toDegrees() == rhs.toDegrees(); } @@ -283,6 +287,10 @@ pub const Angle = union(Tag) { .deg, .rad, .grad, .turn => |v| CSSNumberFns.sign(&v), }; } + + pub fn hash(this: *const @This(), hasher: *std.hash.Wyhash) void { + return css.implementHash(@This(), this, hasher); + } }; /// A CSS [``](https://www.w3.org/TR/css-values-4/#typedef-angle-percentage) value. diff --git a/src/css/values/color.zig b/src/css/values/color.zig index f3a83e4da0..caf3c4bcb4 100644 --- a/src/css/values/color.zig +++ b/src/css/values/color.zig @@ -87,6 +87,8 @@ pub const CssColor = union(enum) { allocator.destroy(this.light); return ret; } + + pub fn __generateHash() void {} }, /// A system color keyword. system: SystemColor, @@ -95,6 +97,10 @@ pub const CssColor = union(enum) { pub const jsFunctionColor = @import("./color_js.zig").jsFunctionColor; + pub fn default() @This() { + return .{ .rgba = RGBA.transparent() }; + } + pub fn eql(this: *const This, other: *const This) bool { if (@intFromEnum(this.*) != @intFromEnum(other.*)) return false; @@ -109,6 +115,10 @@ pub const CssColor = union(enum) { }; } + pub fn hash(this: *const @This(), hasher: *std.hash.Wyhash) void { + return css.implementHash(@This(), this, hasher); + } + pub fn toCss( this: *const This, comptime W: type, @@ -1344,6 +1354,10 @@ pub const RGBA = struct { .alpha = rgb.alphaF32(), }; } + + pub fn hash(this: *const @This(), hasher: *std.hash.Wyhash) void { + return css.implementHash(@This(), this, hasher); + } }; fn clamp_unit_f32(val: f32) u8 { @@ -1403,6 +1417,10 @@ pub const LABColor = union(enum) { .lab = LCH.new(l, a, b, alpha), }; } + + pub fn hash(this: *const @This(), hasher: *std.hash.Wyhash) void { + return css.implementHash(@This(), this, hasher); + } }; /// A color in a predefined color space, e.g. `display-p3`. @@ -1423,6 +1441,10 @@ pub const PredefinedColor = union(enum) { xyz_d50: XYZd50, /// A color in the `xyz-d65` color space. xyz_d65: XYZd65, + + pub fn hash(this: *const @This(), hasher: *std.hash.Wyhash) void { + return css.implementHash(@This(), this, hasher); + } }; /// A floating point representation of color types that @@ -1435,6 +1457,10 @@ pub const FloatColor = union(enum) { hsl: HSL, /// An HWB color. hwb: HWB, + + pub fn hash(this: *const @This(), hasher: *std.hash.Wyhash) void { + return css.implementHash(@This(), this, hasher); + } }; /// A CSS [system color](https://drafts.csswg.org/css-color/#css-system-colors) keyword. @@ -2963,6 +2989,10 @@ pub fn DefineColorspace(comptime T: type) type { .system => null, }; } + + pub fn hash(this: *const T, hasher: *std.hash.Wyhash) void { + return css.implementHash(T, this, hasher); + } }; } diff --git a/src/css/values/gradient.zig b/src/css/values/gradient.zig index 1736efed25..fe97459893 100644 --- a/src/css/values/gradient.zig +++ b/src/css/values/gradient.zig @@ -46,7 +46,7 @@ pub const Gradient = union(enum) { const Closure = struct { location: css.SourceLocation, func: []const u8 }; return input.parseNestedBlock(Gradient, Closure{ .location = location, .func = func }, struct { fn parse( - closure: struct { location: css.SourceLocation, func: []const u8 }, + closure: Closure, input_: *css.Parser, ) Result(Gradient) { // css.todo_stuff.match_ignore_ascii_case @@ -101,22 +101,22 @@ pub const Gradient = union(enum) { .err => |e| return .{ .err = e }, } } }; } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(closure.func, "-moz-linear-gradient")) { - return .{ .result = .{ .linear = switch (LinearGradient.parse(input_, css.VendorPrefix{ .mox = true })) { + return .{ .result = .{ .linear = switch (LinearGradient.parse(input_, css.VendorPrefix{ .moz = true })) { .result => |vv| vv, .err => |e| return .{ .err = e }, } } }; } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(closure.func, "-moz-repeating-linear-gradient")) { - return .{ .result = .{ .repeating_linear = switch (LinearGradient.parse(input_, css.VendorPrefix{ .mox = true })) { + return .{ .result = .{ .repeating_linear = switch (LinearGradient.parse(input_, css.VendorPrefix{ .moz = true })) { .result => |vv| vv, .err => |e| return .{ .err = e }, } } }; } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(closure.func, "-moz-radial-gradient")) { - return .{ .result = .{ .radial = switch (RadialGradient.parse(input_, css.VendorPrefix{ .mox = true })) { + return .{ .result = .{ .radial = switch (RadialGradient.parse(input_, css.VendorPrefix{ .moz = true })) { .result => |vv| vv, .err => |e| return .{ .err = e }, } } }; } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(closure.func, "-moz-repeating-radial-gradient")) { - return .{ .result = .{ .repeating_radial = switch (RadialGradient.parse(input_, css.VendorPrefix{ .mox = true })) { + return .{ .result = .{ .repeating_radial = switch (RadialGradient.parse(input_, css.VendorPrefix{ .moz = true })) { .result => |vv| vv, .err => |e| return .{ .err = e }, } } }; @@ -146,7 +146,7 @@ pub const Gradient = union(enum) { .err => |e| return .{ .err = e }, } } }; } else { - return closure.location.newUnexpectedTokenError(.{ .ident = closure.func }); + return .{ .err = closure.location.newUnexpectedTokenError(.{ .ident = closure.func }) }; } } }.parse); @@ -186,6 +186,30 @@ pub const Gradient = union(enum) { return dest.writeChar(')'); } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } + + pub fn eql(this: *const Gradient, other: *const Gradient) bool { + return css.implementEql(Gradient, this, other); + // if (this.* == .linear and other.* == .linear) { + // return this.linear.eql(&other.linear); + // } else if (this.* == .repeating_linear and other.* == .repeating_linear) { + // return this.repeating_linear.eql(&other.repeating_linear); + // } else if (this.* == .radial and other.* == .radial) { + // return this.radial.eql(&other.radial); + // } else if (this.* == .repeating_radial and other.* == .repeating_radial) { + // return this.repeating_radial.eql(&other.repeating_radial); + // } else if (this.* == .conic and other.* == .conic) { + // return this.conic.eql(&other.conic); + // } else if (this.* == .repeating_conic and other.* == .repeating_conic) { + // return this.repeating_conic.eql(&other.repeating_conic); + // } else if (this.* == .@"webkit-gradient" and other.* == .@"webkit-gradient") { + // return this.@"webkit-gradient".eql(&other.@"webkit-gradient"); + // } + // ret + } }; /// A CSS [`linear-gradient()`](https://www.w3.org/TR/css-images-3/#linear-gradients) or `repeating-linear-gradient()`. @@ -197,11 +221,19 @@ pub const LinearGradient = struct { /// The color stops and transition hints for the gradient. items: ArrayList(GradientItem(LengthPercentage)), + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } + + pub fn eql(this: *const LinearGradient, other: *const LinearGradient) bool { + return this.vendor_prefix.eql(other.vendor_prefix) and this.direction.eql(&other.direction) and css.generic.eqlList(GradientItem(LengthPercentage), &this.items, &other.items); + } + pub fn parse(input: *css.Parser, vendor_prefix: VendorPrefix) Result(LinearGradient) { - const direction = if (input.tryParse(LineDirection.parse, .{vendor_prefix != VendorPrefix{ .none = true }}).asValue()) |dir| direction: { + const direction: LineDirection = if (input.tryParse(LineDirection.parse, .{vendor_prefix.neq(VendorPrefix{ .none = true })}).asValue()) |dir| direction: { if (input.expectComma().asErr()) |e| return .{ .err = e }; break :direction dir; - } else .{ .vertical = .bottom }; + } else LineDirection{ .vertical = .bottom }; const items = switch (parseItems(LengthPercentage, input)) { .result => |vv| vv, .err => |e| return .{ .err = e }, @@ -210,7 +242,7 @@ pub const LinearGradient = struct { } pub fn toCss(this: *const LinearGradient, comptime W: type, dest: *Printer(W), is_prefixed: bool) PrintErr!void { - const angle = switch (this.direction) { + const angle: f32 = switch (this.direction) { .vertical => |v| switch (v) { .bottom => 180.0, .top => 0.0, @@ -222,14 +254,14 @@ pub const LinearGradient = struct { // We can omit `to bottom` or `180deg` because it is the default. if (angle == 180.0) { // todo_stuff.depth - try serializeItems(&this.items, W, dest); + try serializeItems(LengthPercentage, &this.items, W, dest); } // If we have `to top` or `0deg`, and all of the positions and hints are percentages, // we can flip the gradient the other direction and omit the direction. else if (angle == 0.0 and dest.minify and brk: { for (this.items.items) |*item| { if (item.* == .hint and item.hint != .percentage) break :brk false; - if (item.* == .color_stop and item.color_stop.position != null and item.color_stop.position != .percetage) break :brk false; + if (item.* == .color_stop and item.color_stop.position != null and item.color_stop.position.? != .percentage) break :brk false; } break :brk true; }) { @@ -237,7 +269,7 @@ pub const LinearGradient = struct { dest.allocator, this.items.items.len, ) catch bun.outOfMemory(); - defer flipped_items.deinit(); + defer flipped_items.deinit(dest.allocator); var i: usize = this.items.items.len; while (i > 0) { @@ -245,22 +277,22 @@ pub const LinearGradient = struct { const item = &this.items.items[i]; switch (item.*) { .hint => |*h| switch (h.*) { - .percentage => |p| try flipped_items.append(.{ .hint = .{ .percentage = .{ .value = 1.0 - p.v } } }), + .percentage => |p| flipped_items.append(dest.allocator, .{ .hint = .{ .percentage = .{ .v = 1.0 - p.v } } }) catch bun.outOfMemory(), else => unreachable, }, - .color_stop => |*cs| try flipped_items.append(.{ + .color_stop => |*cs| flipped_items.append(dest.allocator, .{ .color_stop = .{ .color = cs.color, - .position = if (cs.position) |*p| switch (p) { - .percentage => |perc| .{ .percentage = .{ .value = 1.0 - perc.value } }, + .position = if (cs.position) |*p| switch (p.*) { + .percentage => |perc| .{ .percentage = .{ .v = 1.0 - perc.v } }, else => unreachable, } else null, }, - }), + }) catch bun.outOfMemory(), } } - try serializeItems(&flipped_items, W, dest); + serializeItems(LengthPercentage, &flipped_items, W, dest) catch return dest.addFmtError(); } else { if ((this.direction != .vertical or this.direction.vertical != .bottom) and (this.direction != .angle or this.direction.angle.deg != 180.0)) @@ -269,7 +301,7 @@ pub const LinearGradient = struct { try dest.delim(',', false); } - try serializeItems(&this.items, W, dest); + serializeItems(LengthPercentage, &this.items, W, dest) catch return dest.addFmtError(); } } }; @@ -285,6 +317,10 @@ pub const RadialGradient = struct { /// The color stops and transition hints for the gradient. items: ArrayList(GradientItem(LengthPercentage)), + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } + pub fn parse(input: *css.Parser, vendor_prefix: VendorPrefix) Result(RadialGradient) { // todo_stuff.depth const shape = switch (input.tryParse(EndingShape.parse, .{})) { @@ -337,7 +373,14 @@ pub const RadialGradient = struct { try dest.delim(',', false); } - try serializeItems(&this.items, W, dest); + try serializeItems(LengthPercentage, &this.items, W, dest); + } + + pub fn eql(this: *const RadialGradient, other: *const RadialGradient) bool { + return this.vendor_prefix.eql(other.vendor_prefix) and + this.shape.eql(&other.shape) and + this.position.eql(&other.position) and + css.generic.eqlList(GradientItem(LengthPercentage), &this.items, &other.items); } }; @@ -350,6 +393,10 @@ pub const ConicGradient = struct { /// The color stops and transition hints for the gradient. items: ArrayList(GradientItem(AnglePercentage)), + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } + pub fn parse(input: *css.Parser) Result(ConicGradient) { const angle = input.tryParse(struct { inline fn parse(i: *css.Parser) Result(Angle) { @@ -367,7 +414,7 @@ pub const ConicGradient = struct { } }.parse, .{}).unwrapOr(Position.center()); - if (angle != .{ .deg = 0.0 } or !std.meta.eql(position, Position.center())) { + if (!angle.eql(&Angle{ .deg = 0.0 }) or !std.meta.eql(position, Position.center())) { if (input.expectComma().asErr()) |e| return .{ .err = e }; } @@ -402,6 +449,12 @@ pub const ConicGradient = struct { return try serializeItems(AnglePercentage, &this.items, W, dest); } + + pub fn eql(this: *const ConicGradient, other: *const ConicGradient) bool { + return this.angle.eql(&other.angle) and + this.position.eql(&other.position) and + css.generic.eqlList(GradientItem(AnglePercentage), &this.items, &other.items); + } }; /// A legacy `-webkit-gradient()`. @@ -414,6 +467,10 @@ pub const WebKitGradient = union(enum) { to: WebKitGradientPoint, /// The color stops in the gradient. stops: ArrayList(WebKitColorStop), + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } }, /// A radial `-webkit-gradient()`. radial: struct { @@ -427,8 +484,29 @@ pub const WebKitGradient = union(enum) { r1: CSSNumber, /// The color stops in the gradient. stops: ArrayList(WebKitColorStop), + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } }, + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } + + pub fn eql(this: *const WebKitGradient, other: *const WebKitGradient) bool { + return switch (this.*) { + .linear => |*a| switch (other.*) { + .linear => a.from.eql(&other.linear.from) and a.to.eql(&other.linear.to) and css.generic.eqlList(WebKitColorStop, &a.stops, &other.linear.stops), + else => false, + }, + .radial => |*a| switch (other.*) { + .radial => a.from.eql(&other.radial.from) and a.to.eql(&other.radial.to) and a.r0 == other.radial.r0 and a.r1 == other.radial.r1 and css.generic.eqlList(WebKitColorStop, &a.stops, &other.radial.stops), + else => false, + }, + }; + } + pub fn parse(input: *css.Parser) Result(WebKitGradient) { const location = input.currentSourceLocation(); const ident = switch (input.expectIdent()) { @@ -517,11 +595,11 @@ pub const WebKitGradient = union(enum) { try dest.delim(',', false); try radial.from.toCss(W, dest); try dest.delim(',', false); - try radial.r0.toCss(W, dest); + try CSSNumberFns.toCss(&radial.r0, W, dest); try dest.delim(',', false); try radial.to.toCss(W, dest); try dest.delim(',', false); - try radial.r1.toCss(W, dest); + try CSSNumberFns.toCss(&radial.r1, W, dest); for (radial.stops.items) |*stop| { try dest.delim(',', false); try stop.toCss(W, dest); @@ -547,9 +625,38 @@ pub const LineDirection = union(enum) { horizontal: HorizontalPositionKeyword, /// A vertical position keyword, e.g. `top` or `bottom`. vertical: VerticalPositionKeyword, + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } }, - pub fn parse(input: *css.Parser, is_prefixed: bool) Result(Position) { + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } + + pub fn eql(this: *const LineDirection, other: *const LineDirection) bool { + return switch (this.*) { + .angle => |*a| switch (other.*) { + .angle => a.eql(&other.angle), + else => false, + }, + .horizontal => |*v| switch (other.*) { + .horizontal => v.* == other.horizontal, + else => false, + }, + .vertical => |*v| switch (other.*) { + .vertical => v.* == other.vertical, + else => false, + }, + .corner => |*c| switch (other.*) { + .corner => c.horizontal == other.corner.horizontal and c.vertical == other.corner.vertical, + else => false, + }, + }; + } + + pub fn parse(input: *css.Parser, is_prefixed: bool) Result(LineDirection) { // Spec allows unitless zero angles for gradients. // https://w3c.github.io/csswg-drafts/css-images-3/#linear-gradient-syntax if (input.tryParse(Angle.parseWithUnitlessZero, .{}).asValue()) |angle| { @@ -588,7 +695,7 @@ pub const LineDirection = union(enum) { .angle => |*angle| try angle.toCss(W, dest), .horizontal => |*k| { if (dest.minify) { - try dest.writeStr(switch (k) { + try dest.writeStr(switch (k.*) { .left => "270deg", .right => "90deg", }); @@ -601,7 +708,7 @@ pub const LineDirection = union(enum) { }, .vertical => |*k| { if (dest.minify) { - try dest.writeStr(switch (k) { + try dest.writeStr(switch (k.*) { .top => "0deg", .bottom => "180deg", }); @@ -641,6 +748,23 @@ pub fn GradientItem(comptime D: type) type { .hint => |*h| try css.generic.toCss(D, h, W, dest), }; } + + pub fn eql(this: *const GradientItem(D), other: *const GradientItem(D)) bool { + return switch (this.*) { + .color_stop => |*a| switch (other.*) { + .color_stop => a.eql(&other.color_stop), + else => false, + }, + .hint => |*a| switch (other.*) { + .hint => css.generic.eql(D, a, &other.hint), + else => false, + }, + }; + } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } }; } @@ -653,9 +777,29 @@ pub const EndingShape = union(enum) { /// A circle. circle: Circle, + pub usingnamespace css.DeriveParse(@This()); + pub usingnamespace css.DeriveToCss(@This()); + pub fn default() EndingShape { return .{ .ellipse = .{ .extent = .@"farthest-corner" } }; } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } + + pub fn eql(this: *const EndingShape, other: *const EndingShape) bool { + return switch (this.*) { + .ellipse => |*a| switch (other.*) { + .ellipse => a.eql(&other.ellipse), + else => false, + }, + .circle => |*a| switch (other.*) { + .circle => a.eql(&other.circle), + else => false, + }, + }; + } }; /// An x/y position within a legacy `-webkit-gradient()`. @@ -682,6 +826,14 @@ pub const WebKitGradientPoint = struct { try dest.writeChar(' '); return try this.y.toCss(W, dest); } + + pub fn eql(this: *const WebKitGradientPoint, other: *const WebKitGradientPoint) bool { + return this.x.eql(&other.x) and this.y.eql(&other.y); + } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } }; /// A keyword or number within a [WebKitGradientPoint](WebKitGradientPoint). @@ -722,7 +874,7 @@ pub fn WebKitGradientPointComponent(comptime S: type) type { } }, .number => |*lp| { - if (lp == .percentage and lp.percentage.value == 0.0) { + if (lp.* == .percentage and lp.percentage.v == 0.0) { try dest.writeChar('0'); } else { try lp.toCss(W, dest); @@ -738,6 +890,23 @@ pub fn WebKitGradientPointComponent(comptime S: type) type { }, } } + + pub fn eql(this: *const This, other: *const This) bool { + return switch (this.*) { + .center => switch (other.*) { + .center => true, + else => false, + }, + .number => |*a| switch (other.*) { + .number => a.eql(&other.number), + else => false, + }, + .side => |*a| switch (other.*) { + .side => |*b| a.eql(&b.*), + else => false, + }, + }; + } }; } @@ -776,7 +945,7 @@ pub const WebKitColorStop = struct { } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(closure.function, "to")) position: { break :position 1.0; } else { - return closure.loc.newUnexpectedTokenError(.{ .ident = closure.function }); + return .{ .err = closure.loc.newUnexpectedTokenError(.{ .ident = closure.function }) }; }; const color = switch (CssColor.parse(i)) { .result => |vv| vv, @@ -803,6 +972,14 @@ pub const WebKitColorStop = struct { } try dest.writeChar(')'); } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } + + pub fn eql(this: *const WebKitColorStop, other: *const WebKitColorStop) bool { + return css.implementEql(WebKitColorStop, this, other); + } }; /// A [``](https://www.w3.org/TR/css-images-4/#color-stop-syntax) within a gradient. @@ -838,6 +1015,14 @@ pub fn ColorStop(comptime D: type) type { } return; } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } + + pub fn eql(this: *const This, other: *const This) bool { + return this.color.eql(&other.color) and css.generic.eql(?D, &this.position, &other.position); + } }; } @@ -851,6 +1036,10 @@ pub const Ellipse = union(enum) { x: LengthPercentage, /// The y-radius of the ellipse. y: LengthPercentage, + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } }, /// A shape extent keyword. extent: ShapeExtent, @@ -907,6 +1096,14 @@ pub const Ellipse = union(enum) { .extent => |*e| try e.toCss(W, dest), }; } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } + + pub fn eql(this: *const Ellipse, other: *const Ellipse) bool { + return this.size.x.eql(&other.size.x) and this.size.y.eql(&other.size.y) and this.extent.eql(&other.extent); + } }; pub const ShapeExtent = enum { @@ -919,6 +1116,10 @@ pub const ShapeExtent = enum { /// The farthest corner of the box from the gradient's center. @"farthest-corner", + pub fn eql(this: *const ShapeExtent, other: *const ShapeExtent) bool { + return this.* == other.*; + } + pub fn asStr(this: *const @This()) []const u8 { return css.enum_property_util.asStr(@This(), this); } @@ -927,6 +1128,10 @@ pub const ShapeExtent = enum { return css.enum_property_util.parse(@This(), input); } + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } + pub fn toCss(this: *const @This(), comptime W: type, dest: *Printer(W)) PrintErr!void { return css.enum_property_util.toCss(@This(), this, W, dest); } @@ -983,6 +1188,14 @@ pub const Circle = union(enum) { }, }; } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } + + pub fn eql(this: *const Circle, other: *const Circle) bool { + return this.radius.eql(&other.radius) and this.extent.eql(&other.extent); + } }; pub fn parseItems(comptime D: type, input: *css.Parser) Result(ArrayList(GradientItem(D))) { @@ -993,13 +1206,14 @@ pub fn parseItems(comptime D: type, input: *css.Parser) Result(ArrayList(Gradien const Closure = struct { items: *ArrayList(GradientItem(D)), seen_stop: *bool }; if (input.parseUntilBefore( css.Delimiters{ .comma = true }, + void, Closure{ .items = &items, .seen_stop = &seen_stop }, struct { fn parse(closure: Closure, i: *css.Parser) Result(void) { if (closure.seen_stop.*) { if (i.tryParse(comptime css.generic.parseFor(D), .{}).asValue()) |hint| { closure.seen_stop.* = false; - closure.items.append(.{ .hint = hint }) catch bun.outOfMemory(); + closure.items.append(i.allocator(), .{ .hint = hint }) catch bun.outOfMemory(); return Result(void).success; } } @@ -1009,15 +1223,15 @@ pub fn parseItems(comptime D: type, input: *css.Parser) Result(ArrayList(Gradien .err => |e| return .{ .err = e }, }; - if (i.tryParse(comptime css.generic.parseFor(D), .{})) |position| { + if (i.tryParse(comptime css.generic.parseFor(D), .{}).asValue()) |position| { const color = stop.color.deepClone(i.allocator()); - closure.items.append(.{ .color_stop = stop }) catch bun.outOfMemory(); - closure.items.append(.{ .color_stop = .{ + closure.items.append(i.allocator(), .{ .color_stop = stop }) catch bun.outOfMemory(); + closure.items.append(i.allocator(), .{ .color_stop = .{ .color = color, .position = position, } }) catch bun.outOfMemory(); } else { - closure.items.append(.{ .color_stop = stop }) catch bun.outOfMemory(); + closure.items.append(i.allocator(), .{ .color_stop = stop }) catch bun.outOfMemory(); } closure.seen_stop.* = true; @@ -1027,7 +1241,7 @@ pub fn parseItems(comptime D: type, input: *css.Parser) Result(ArrayList(Gradien ).asErr()) |e| return .{ .err = e }; if (input.next().asValue()) |tok| { - if (tok == .comma) continue; + if (tok.* == .comma) continue; bun.unreachablePanic("expected a comma after parsing a gradient", .{}); } else { break; @@ -1047,7 +1261,7 @@ pub fn serializeItems( var last: ?*const GradientItem(D) = null; for (items.items) |*item| { // Skip useless hints - if (item.* == .hint and item.hint == .percentage and item.hint.percentage.value == 0.5) { + if (item.* == .hint and item.hint == .percentage and item.hint.percentage.v == 0.5) { continue; } diff --git a/src/css/values/ident.zig b/src/css/values/ident.zig index 05943424ee..ee861540c9 100644 --- a/src/css/values/ident.zig +++ b/src/css/values/ident.zig @@ -25,6 +25,10 @@ pub const DashedIdentReference = struct { /// Only enabled when the CSS modules `dashed_idents` option is turned on. from: ?Specifier, + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } + pub fn parseWithOptions(input: *css.Parser, options: *const css.ParserOptions) Result(DashedIdentReference) { const ident = switch (DashedIdentFns.parse(input)) { .result => |vv| vv, @@ -55,6 +59,10 @@ pub const DashedIdentReference = struct { return dest.writeDashedIdent(&this.ident, false); } + + pub fn hash(this: *const @This(), hasher: *std.hash.Wyhash) void { + return css.implementHash(@This(), this, hasher); + } }; pub const DashedIdentFns = DashedIdent; @@ -65,6 +73,22 @@ pub const DashedIdentFns = DashedIdent; pub const DashedIdent = struct { v: []const u8, + pub fn HashMap(comptime V: type) type { + return std.ArrayHashMapUnmanaged( + DashedIdent, + V, + struct { + pub fn hash(_: @This(), s: DashedIdent) u32 { + return std.array_hash_map.hashString(s.v); + } + pub fn eql(_: @This(), a: DashedIdent, b: DashedIdent, _: usize) bool { + return bun.strings.eql(a, b); + } + }, + false, + ); + } + pub fn parse(input: *css.Parser) Result(DashedIdent) { const location = input.currentSourceLocation(); const ident = switch (input.expectIdent()) { @@ -81,6 +105,14 @@ pub const DashedIdent = struct { pub fn toCss(this: *const DashedIdent, comptime W: type, dest: *Printer(W)) PrintErr!void { return dest.writeDashedIdent(this, true); } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } + + pub fn hash(this: *const @This(), hasher: *std.hash.Wyhash) void { + return css.implementHash(@This(), this, hasher); + } }; /// A CSS [``](https://www.w3.org/TR/css-values-4/#css-css-identifier). @@ -99,6 +131,14 @@ pub const Ident = struct { pub fn toCss(this: *const Ident, comptime W: type, dest: *Printer(W)) PrintErr!void { return css.serializer.serializeIdentifier(this.v, dest) catch return dest.addFmtError(); } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } + + pub fn hash(this: *const @This(), hasher: *std.hash.Wyhash) void { + return css.implementHash(@This(), this, hasher); + } }; pub const CustomIdentFns = CustomIdent; @@ -143,6 +183,14 @@ pub const CustomIdent = struct { false; return dest.writeIdent(this.v, css_module_custom_idents_enabled); } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } + + pub fn hash(this: *const @This(), hasher: *std.hash.Wyhash) void { + return css.implementHash(@This(), this, hasher); + } }; /// A list of CSS [``](https://www.w3.org/TR/css-values-4/#custom-idents) values. diff --git a/src/css/values/image.zig b/src/css/values/image.zig index 685a18bfb0..3ac094b9bf 100644 --- a/src/css/values/image.zig +++ b/src/css/values/image.zig @@ -23,21 +23,50 @@ pub const Image = union(enum) { /// A gradient. gradient: *Gradient, /// An `image-set()`. - image_set: *ImageSet, + image_set: ImageSet, - // pub usingnamespace css.DeriveParse(@This()); - // pub usingnamespace css.DeriveToCss(@This()); + pub usingnamespace css.DeriveParse(@This()); + pub usingnamespace css.DeriveToCss(@This()); - pub fn parse(input: *css.Parser) Result(Image) { - _ = input; // autofix - @panic(css.todo_stuff.depth); + pub fn default() Image { + return .none; } - pub fn toCss(this: *const Image, comptime W: type, dest: *css.Printer(W)) css.PrintErr!void { - _ = this; // autofix - _ = dest; // autofix - @panic(css.todo_stuff.depth); + pub inline fn eql(this: *const Image, other: *const Image) bool { + return switch (this.*) { + .none => switch (other.*) { + .none => true, + else => false, + }, + .url => |*a| switch (other.*) { + .url => a.eql(&other.url), + else => false, + }, + .image_set => |*a| switch (other.*) { + .image_set => a.eql(&other.image_set), + else => false, + }, + .gradient => |a| switch (other.*) { + .gradient => a.eql(other.gradient), + else => false, + }, + }; } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } + + // pub fn parse(input: *css.Parser) Result(Image) { + // _ = input; // autofix + // @panic(css.todo_stuff.depth); + // } + + // pub fn toCss(this: *const Image, comptime W: type, dest: *css.Printer(W)) css.PrintErr!void { + // _ = this; // autofix + // _ = dest; // autofix + // @panic(css.todo_stuff.depth); + // } }; /// A CSS [`image-set()`](https://drafts.csswg.org/css-images-4/#image-set-notation) value. @@ -53,13 +82,16 @@ pub const ImageSet = struct { pub fn parse(input: *css.Parser) Result(ImageSet) { const location = input.currentSourceLocation(); - const f = input.expectFunction(); + const f = switch (input.expectFunction()) { + .result => |v| v, + .err => |e| return .{ .err = e }, + }; const vendor_prefix = vendor_prefix: { // todo_stuff.match_ignore_ascii_case - if (bun.strings.eqlCaseInsensitiveASCIIICheckLength("image-set", css.VendorPrefix{.none})) { - break :vendor_prefix .none; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength("-webkit-image-set", css.VendorPrefix{.none})) { - break :vendor_prefix .webkit; + if (bun.strings.eqlCaseInsensitiveASCIIICheckLength("image-set", f)) { + break :vendor_prefix VendorPrefix{ .none = true }; + } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength("-webkit-image-set", f)) { + break :vendor_prefix VendorPrefix{ .webkit = true }; } else return .{ .err = location.newUnexpectedTokenError(.{ .ident = f }) }; }; @@ -90,10 +122,18 @@ pub const ImageSet = struct { } else { try dest.delim(',', false); } - try option.toCss(W, dest); + try option.toCss(W, dest, this.vendor_prefix.neq(VendorPrefix{ .none = true })); } return dest.writeChar(')'); } + + pub fn eql(this: *const ImageSet, other: *const ImageSet) bool { + return this.vendor_prefix.eql(other.vendor_prefix) and css.generic.eqlList(ImageSetOption, &this.options, &other.options); + } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } }; /// An image option within the `image-set()` function. See [ImageSet](ImageSet). @@ -106,13 +146,21 @@ pub const ImageSetOption = struct { file_type: ?[]const u8, pub fn parse(input: *css.Parser) Result(ImageSetOption) { + const start_position = input.input.tokenizer.getPosition(); const loc = input.currentSourceLocation(); - const image = if (input.tryParse(css.Parser.expectUrlOrString, .{}).asValue()) |url| - Image{ .url = Url{ - .url = url, - .loc = loc, - } } - else switch (@call(.auto, @field(Image, "parse"), .{input})) { // For some reason, `Image.parse` makes zls crash, using this syntax until that's fixed + const image = if (input.tryParse(css.Parser.expectUrlOrString, .{}).asValue()) |url| brk: { + const record_idx = switch (input.addImportRecordForUrl( + url, + start_position, + )) { + .result => |idx| idx, + .err => |e| return .{ .err = e }, + }; + break :brk Image{ .url = Url{ + .import_record_idx = record_idx, + .loc = css.dependencies.Location.fromSourceLocation(loc), + } }; + } else switch (@call(.auto, @field(Image, "parse"), .{input})) { // For some reason, `Image.parse` makes zls crash, using this syntax until that's fixed .result => |vv| vv, .err => |e| return .{ .err = e }, }; @@ -139,14 +187,14 @@ pub const ImageSetOption = struct { dest: *css.Printer(W), is_prefixed: bool, ) PrintErr!void { - if (this.image.* == .url and !is_prefixed) { + if (this.image == .url and !is_prefixed) { const _dep: ?UrlDependency = if (dest.dependencies != null) - UrlDependency.new(dest.allocator, &this.image.url.url, dest.filename(), try dest.getImportRecords()) + UrlDependency.new(dest.allocator, &this.image.url, dest.filename(), try dest.getImportRecords()) else null; if (_dep) |dep| { - try css.serializer.serializeString(dep.placeholder, W, dest); + css.serializer.serializeString(dep.placeholder, dest) catch return dest.addFmtError(); if (dest.dependencies) |*dependencies| { dependencies.append( dest.allocator, @@ -154,7 +202,7 @@ pub const ImageSetOption = struct { ) catch bun.outOfMemory(); } } else { - try css.serializer.serializeString(this.image.url.url, W, dest); + css.serializer.serializeString(try dest.getImportRecordUrl(this.image.url.import_record_idx), dest) catch return dest.addFmtError(); } } else { try this.image.toCss(W, dest); @@ -178,10 +226,23 @@ pub const ImageSetOption = struct { if (this.file_type) |file_type| { try dest.writeStr(" type("); - try css.serializer.serializeString(file_type, W, dest); + css.serializer.serializeString(file_type, dest) catch return dest.addFmtError(); try dest.writeChar(')'); } } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } + + pub fn eql(lhs: *const ImageSetOption, rhs: *const ImageSetOption) bool { + return lhs.image.eql(&rhs.image) and lhs.resolution.eql(&rhs.resolution) and (brk: { + if (lhs.file_type != null and rhs.file_type != null) { + break :brk bun.strings.eql(lhs.file_type.?, rhs.file_type.?); + } + break :brk false; + }); + } }; fn parseFileType(input: *css.Parser) Result([]const u8) { diff --git a/src/css/values/length.zig b/src/css/values/length.zig index 6b12a6c0a0..eec8bd9a80 100644 --- a/src/css/values/length.zig +++ b/src/css/values/length.zig @@ -21,12 +21,20 @@ pub const LengthOrNumber = union(enum) { pub usingnamespace css.DeriveParse(@This()); pub usingnamespace css.DeriveToCss(@This()); + pub fn default() LengthOrNumber { + return .{ .number = 0.0 }; + } + pub fn eql(this: *const @This(), other: *const @This()) bool { return switch (this.*) { .number => |*n| n.* == other.number, .length => |*l| l.eql(&other.length), }; } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } }; pub const LengthPercentage = DimensionPercentage(LengthValue); @@ -36,6 +44,17 @@ pub const LengthPercentageOrAuto = union(enum) { auto, /// A [``](https://www.w3.org/TR/css-values-4/#typedef-length-percentage). length: LengthPercentage, + + pub usingnamespace css.DeriveParse(@This()); + pub usingnamespace css.DeriveToCss(@This()); + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } + + pub inline fn deepClone(this: *const @This(), allocator: Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } }; const PX_PER_IN: f32 = 96.0; @@ -198,6 +217,19 @@ pub const LengthValue = union(enum) { return css.serializer.serializeDimension(value, unit, W, dest); } + pub fn isZero(this: *const LengthValue) bool { + inline for (bun.meta.EnumFields(@This())) |field| { + if (@intFromEnum(this.*) == field.value) { + return @field(this, field.name) == 0.0; + } + } + unreachable; + } + + pub fn zero() LengthValue { + return .{ .px = 0.0 }; + } + /// Attempts to convert the value to pixels. /// Returns `None` if the conversion is not possible. pub fn toPx(this: *const @This()) ?CSSNumber { @@ -353,6 +385,27 @@ pub const LengthValue = union(enum) { } return null; } + + pub fn hash(this: *const @This(), hasher: *std.hash.Wyhash) void { + return css.implementHash(@This(), this, hasher); + } + + pub fn tryAdd(this: *const LengthValue, _: std.mem.Allocator, rhs: *const LengthValue) ?LengthValue { + if (@intFromEnum(this.*) == @intFromEnum(rhs.*)) { + inline for (bun.meta.EnumFields(LengthValue)) |field| { + if (field.value == @intFromEnum(this.*)) { + return @unionInit(LengthValue, field.name, @field(this, field.name) + @field(rhs, field.name)); + } + } + unreachable; + } + if (this.toPx()) |a| { + if (rhs.toPx()) |b| { + return .{ .px = a + b }; + } + } + return null; + } }; /// A CSS [``](https://www.w3.org/TR/css-values-4/#lengths) value, with support for `calc()`. diff --git a/src/css/values/percentage.zig b/src/css/values/percentage.zig index abf48b46e3..6c30d0621f 100644 --- a/src/css/values/percentage.zig +++ b/src/css/values/percentage.zig @@ -195,21 +195,17 @@ pub fn DimensionPercentage(comptime D: type) type { } pub fn zero() This { - return .{ - .percentage = .{ - .value = switch (D) { - f32 => 0.0, - else => @compileError("TODO implement .zero() for " + @typeName(D)), - }, - }, - }; + return This{ .dimension = switch (D) { + f32 => 0.0, + else => D.zero(), + } }; } pub fn isZero(this: *const This) bool { return switch (this.*) { .dimension => |*d| switch (D) { f32 => d == 0.0, - else => @compileError("TODO implement .isZero() for " + @typeName(D)), + else => d.isZero(), }, .percentage => |*p| p.isZero(), else => false, @@ -232,10 +228,178 @@ pub fn DimensionPercentage(comptime D: type) type { } pub fn add(this: This, allocator: std.mem.Allocator, other: This) This { - _ = this; // autofix - _ = allocator; // autofix - _ = other; // autofix - @panic(css.todo_stuff.depth); + // Unwrap calc(...) functions so we can add inside. + // Then wrap the result in a calc(...) again if necessary. + const a = unwrapCalc(this, allocator); + const b = unwrapCalc(other, allocator); + const res = a.addInternal(allocator, b); + return switch (res) { + .calc => |c| switch (c.*) { + .value => |l| l.*, + .function => |f| if (f.* != .calc) .{ + .calc = bun.create(allocator, Calc(DimensionPercentage(D)), .{ + .function = f, + }), + } else .{ + .calc = bun.create(allocator, Calc(DimensionPercentage(D)), .{ + .function = bun.create( + allocator, + css.css_values.calc.MathFunction(DimensionPercentage(D)), + .{ .calc = c.* }, + ), + }), + }, + else => .{ + .calc = bun.create(allocator, Calc(DimensionPercentage(D)), .{ + .function = bun.create( + allocator, + css.css_values.calc.MathFunction(DimensionPercentage(D)), + .{ .calc = c.* }, + ), + }), + }, + }, + else => res, + }; + } + + fn addInternal(this: This, allocator: std.mem.Allocator, other: This) This { + if (this.addRecursive(allocator, &other)) |res| return res; + return this.addImpl(allocator, other); + } + + fn addRecursive(this: *const This, allocator: std.mem.Allocator, other: *const This) ?This { + if (this.* == .dimension and other.* == .dimension) { + if (this.dimension.tryAdd(allocator, &other.dimension)) |res| { + return .{ .dimension = res }; + } + } else if (this.* == .percentage and other.* == .percentage) { + return .{ .percentage = .{ .v = this.percentage.v + other.percentage.v } }; + } else if (this.* == .calc) { + switch (this.calc.*) { + .value => |v| return v.addRecursive(allocator, other), + .sum => |sum| { + const left_calc = This{ .calc = sum.left }; + if (left_calc.addRecursive(allocator, other)) |res| { + return res.add(allocator, This{ .calc = sum.right }); + } + + const right_calc = This{ .calc = sum.right }; + if (right_calc.addRecursive(allocator, other)) |res| { + return (This{ .calc = sum.left }).add(allocator, res); + } + }, + else => {}, + } + } else if (other.* == .calc) { + switch (other.calc.*) { + .value => |v| return this.addRecursive(allocator, v), + .sum => |sum| { + const left_calc = This{ .calc = sum.left }; + if (this.addRecursive(allocator, &left_calc)) |res| { + return res.add(allocator, This{ .calc = sum.right }); + } + + const right_calc = This{ .calc = sum.right }; + if (this.addRecursive(allocator, &right_calc)) |res| { + return (This{ .calc = sum.left }).add(allocator, res); + } + }, + else => {}, + } + } + + return null; + } + + fn addImpl(this: This, allocator: std.mem.Allocator, other: This) This { + var a = this; + var b = other; + + if (a.isZero()) return b; + if (b.isZero()) return a; + + if (a.isSignNegative() and b.isSignPositive()) { + std.mem.swap(This, &a, &b); + } + + if (a == .calc and b == .calc) { + return .{ .calc = bun.create(allocator, Calc(DimensionPercentage(D)), a.calc.add(allocator, b.calc.*)) }; + } else if (a == .calc) { + if (a.calc.* == .value) { + return a.calc.value.add(allocator, b); + } else { + return .{ + .calc = bun.create( + allocator, + Calc(DimensionPercentage(D)), + .{ .sum = .{ + .left = bun.create(allocator, Calc(DimensionPercentage(D)), a.calc.*), + .right = bun.create(allocator, Calc(DimensionPercentage(D)), b.intoCalc(allocator)), + } }, + ), + }; + } + } else if (b == .calc) { + if (b.calc.* == .value) { + return a.add(allocator, b.calc.value.*); + } else { + return .{ + .calc = bun.create( + allocator, + Calc(DimensionPercentage(D)), + .{ .sum = .{ + .left = bun.create(allocator, Calc(DimensionPercentage(D)), a.intoCalc(allocator)), + .right = bun.create(allocator, Calc(DimensionPercentage(D)), b.calc.*), + } }, + ), + }; + } + } else { + return .{ + .calc = bun.create( + allocator, + Calc(DimensionPercentage(D)), + .{ .sum = .{ + .left = bun.create(allocator, Calc(DimensionPercentage(D)), a.intoCalc(allocator)), + .right = bun.create(allocator, Calc(DimensionPercentage(D)), b.intoCalc(allocator)), + } }, + ), + }; + } + } + + inline fn isSignPositive(this: This) bool { + const sign = this.trySign() orelse return false; + return css.signfns.isSignPositive(sign); + } + + inline fn isSignNegative(this: This) bool { + const sign = this.trySign() orelse return false; + return css.signfns.isSignNegative(sign); + } + + fn unwrapCalc(this: This, allocator: std.mem.Allocator) This { + return switch (this) { + .calc => |calc| switch (calc.*) { + .function => |f| switch (f.*) { + .calc => |c2| .{ .calc = bun.create(allocator, Calc(DimensionPercentage(D)), c2) }, + else => .{ .calc = bun.create( + allocator, + Calc(DimensionPercentage(D)), + .{ + .function = bun.create( + allocator, + css.css_values.calc.MathFunction(DimensionPercentage(D)), + f.*, + ), + }, + ) }, + }, + else => .{ .calc = calc }, + }, + else => this, + }; } pub fn partialCmp(this: *const This, other: *const This) ?std.math.Order { @@ -246,7 +410,7 @@ pub fn DimensionPercentage(comptime D: type) type { pub fn trySign(this: *const This) ?f32 { return switch (this.*) { - .dimension => |d| d.trySign(), + .dimension => |*d| css.generic.trySign(@TypeOf(d.*), d), .percentage => |p| p.trySign(), .calc => |c| c.trySign(), }; @@ -275,6 +439,13 @@ pub fn DimensionPercentage(comptime D: type) type { if (this.* == .percentage and other.* == .percentage) return .{ .percentage = Percentage{ .v = op_fn(ctx, this.percentage.v, other.percentage.v) } }; return null; } + + pub fn intoCalc(this: This, allocator: std.mem.Allocator) Calc(DimensionPercentage(D)) { + return switch (this) { + .calc => |calc| calc.*, + else => .{ .value = bun.create(allocator, This, this) }, + }; + } }; } @@ -286,24 +457,37 @@ pub const NumberOrPercentage = union(enum) { percentage: Percentage, // TODO: implement this - // pub usingnamespace css.DeriveParse(@This()); - // pub usingnamespace css.DeriveToCss(@This()); + pub usingnamespace css.DeriveParse(@This()); + pub usingnamespace css.DeriveToCss(@This()); - pub fn parse(input: *css.Parser) Result(NumberOrPercentage) { - _ = input; // autofix - @panic(css.todo_stuff.depth); - } + // pub fn parse(input: *css.Parser) Result(NumberOrPercentage) { + // _ = input; // autofix + // @panic(css.todo_stuff.depth); + // } - pub fn toCss(this: *const NumberOrPercentage, comptime W: type, dest: *css.Printer(W)) css.PrintErr!void { - _ = this; // autofix - _ = dest; // autofix - @panic(css.todo_stuff.depth); + // pub fn toCss(this: *const NumberOrPercentage, comptime W: type, dest: *css.Printer(W)) css.PrintErr!void { + // _ = this; // autofix + // _ = dest; // autofix + // @panic(css.todo_stuff.depth); + // } + + pub fn eql(this: *const NumberOrPercentage, other: *const NumberOrPercentage) bool { + return switch (this.*) { + .number => |*a| switch (other.*) { + .number => a.* == other.number, + .percentage => false, + }, + .percentage => |*a| switch (other.*) { + .number => false, + .percentage => a.eql(&other.percentage), + }, + }; } pub fn intoF32(this: *const @This()) f32 { return switch (this.*) { .number => this.number, - .percentage => this.percentage.v(), + .percentage => this.percentage.v, }; } }; diff --git a/src/css/values/position.zig b/src/css/values/position.zig index 9a0e1058d2..35c06913b2 100644 --- a/src/css/values/position.zig +++ b/src/css/values/position.zig @@ -10,6 +10,7 @@ const CSSNumberFns = css.css_values.number.CSSNumberFns; const Calc = css.css_values.calc.Calc; const DimensionPercentage = css.css_values.percentage.DimensionPercentage; const LengthPercentage = css.css_values.length.LengthPercentage; +const Percentage = css.css_values.percentage.Percentage; /// A CSS `` value, /// as used in the `background-position` property, gradients, masks, etc. @@ -19,15 +20,6 @@ pub const Position = struct { /// The y-position. y: VerticalPosition, - /// Returns whether both the x and y positions are centered. - pub fn isCenter(this: *const @This()) bool { - this.x.isCenter() and this.y.isCenter(); - } - - pub fn center() Position { - return .{ .x = .center, .y = .center }; - } - pub fn parse(input: *css.Parser) Result(Position) { // Try parsing a horizontal position first if (input.tryParse(HorizontalPosition.parse, .{}).asValue()) |horizontal_pos| { @@ -152,15 +144,15 @@ pub const Position = struct { } pub fn toCss(this: *const Position, comptime W: type, dest: *css.Printer(W)) css.PrintErr!void { - if (this.x == .side and this.y == .length and this.x.side != .left) { + if (this.x == .side and this.y == .length and this.x.side.side != .left) { try this.x.toCss(W, dest); try dest.writeStr(" top "); try this.y.length.toCss(W, dest); - } else if (this.x == .side and this.x.side != .left and this.y.isCenter()) { + } else if (this.x == .side and this.x.side.side != .left and this.y.isCenter()) { // If there is a side keyword with an offset, "center" must be a keyword not a percentage. try this.x.toCss(W, dest); try dest.writeStr(" center"); - } else if (this.x == .length and this.y == .side and this.y.side != .top) { + } else if (this.x == .length and this.y == .side and this.y.side.side != .top) { try dest.writeStr("left "); try this.x.length.toCss(W, dest); try dest.writeStr(" "); @@ -175,7 +167,7 @@ pub const Position = struct { const p: LengthPercentage = this.x.side.side.intoLengthPercentage(); try p.toCss(W, dest); } else if (this.y == .side and this.y.side.offset == null and this.x.isCenter()) { - this.y.toCss(W, dest); + try this.y.toCss(W, dest); } else if (this.x == .side and this.x.side.offset == null and this.y == .side and this.y.side.offset == null) { const x: LengthPercentage = this.x.side.side.intoLengthPercentage(); const y: LengthPercentage = this.y.side.side.intoLengthPercentage(); @@ -206,7 +198,6 @@ pub const Position = struct { } }, .center => break :x_len &fifty, - else => {}, } break :x_len null; }; @@ -214,7 +205,7 @@ pub const Position = struct { const y_len: ?*const LengthPercentage = y_len: { switch (this.y) { .side => |side| { - if (side.side == .left) { + if (side.side == .top) { if (side.offset) |*offset| { if (offset.isZero()) { break :y_len &zero; @@ -232,7 +223,6 @@ pub const Position = struct { } }, .center => break :y_len &fifty, - else => {}, } break :y_len null; }; @@ -248,6 +238,34 @@ pub const Position = struct { } } } + + pub fn default() @This() { + return .{ + .x = HorizontalPosition{ .length = LengthPercentage{ .percentage = .{ .v = 0.0 } } }, + .y = VerticalPosition{ .length = LengthPercentage{ .percentage = .{ .v = 0.0 } } }, + }; + } + + /// Returns whether both the x and y positions are centered. + pub fn isCenter(this: *const @This()) bool { + return this.x.isCenter() and this.y.isCenter(); + } + + pub fn center() Position { + return .{ .x = .center, .y = .center }; + } + + pub fn eql(this: *const Position, other: *const Position) bool { + return this.x.eql(&other.x) and this.y.eql(&other.y); + } + + pub fn isZero(this: *const Position) bool { + return this.x.isZero() and this.y.isZero(); + } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } }; pub fn PositionComponent(comptime S: type) type { @@ -262,15 +280,45 @@ pub fn PositionComponent(comptime S: type) type { side: S, /// Offset from the side. offset: ?LengthPercentage, + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } }, const This = @This(); + pub fn isZero(this: *const This) bool { + if (this.* == .length and this.length.isZero()) return true; + return false; + } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } + + pub fn eql(this: *const This, other: *const This) bool { + return switch (this.*) { + .center => switch (other.*) { + .center => true, + else => false, + }, + .length => |*a| switch (other.*) { + .length => a.eql(&other.length), + else => false, + }, + .side => |*a| switch (other.*) { + .side => a.side.eql(&other.side.side) and css.generic.eql(?LengthPercentage, &a.offset, &other.side.offset), + else => false, + }, + }; + } + pub fn parse(input: *css.Parser) Result(This) { if (input.tryParse( struct { fn parse(i: *css.Parser) Result(void) { - if (i.expectIdentMatching("center").asErr()) |e| return .{ .err = e }; + return i.expectIdentMatching("center"); } }.parse, .{}, @@ -314,7 +362,7 @@ pub fn PositionComponent(comptime S: type) type { switch (this.*) { .center => return true, .length => |*l| { - if (l == .percentage) return l.percentage.v == 0.5; + if (l.* == .percentage) return l.percentage.v == 0.5; }, else => {}, } @@ -329,6 +377,10 @@ pub const HorizontalPositionKeyword = enum { /// The `right` keyword. right, + pub fn eql(this: *const HorizontalPositionKeyword, other: *const HorizontalPositionKeyword) bool { + return this.* == other.*; + } + pub fn asStr(this: *const @This()) []const u8 { return css.enum_property_util.asStr(@This(), this); } @@ -355,6 +407,10 @@ pub const VerticalPositionKeyword = enum { /// The `bottom` keyword. bottom, + pub fn eql(this: *const VerticalPositionKeyword, other: *const VerticalPositionKeyword) bool { + return this.* == other.*; + } + pub fn asStr(this: *const @This()) []const u8 { return css.enum_property_util.asStr(@This(), this); } @@ -366,6 +422,13 @@ pub const VerticalPositionKeyword = enum { pub fn toCss(this: *const @This(), comptime W: type, dest: *Printer(W)) PrintErr!void { return css.enum_property_util.toCss(@This(), this, W, dest); } + + pub fn intoLengthPercentage(this: *const @This()) LengthPercentage { + return switch (this.*) { + .top => LengthPercentage.zero(), + .bottom => LengthPercentage{ .percentage = Percentage{ .v = 1.0 } }, + }; + } }; pub const HorizontalPosition = PositionComponent(HorizontalPositionKeyword); diff --git a/src/css/values/ratio.zig b/src/css/values/ratio.zig index 492eb641ea..8784f898fd 100644 --- a/src/css/values/ratio.zig +++ b/src/css/values/ratio.zig @@ -68,4 +68,8 @@ pub const Ratio = struct { pub fn addF32(this: Ratio, _: std.mem.Allocator, other: f32) Ratio { return .{ .numerator = this.numerator + other, .denominator = this.denominator }; } + + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } }; diff --git a/src/css/values/rect.zig b/src/css/values/rect.zig index fe13e00719..28b281c6d7 100644 --- a/src/css/values/rect.zig +++ b/src/css/values/rect.zig @@ -33,6 +33,10 @@ fn needsDeinit(comptime T: type) bool { css.css_values.percentage.NumberOrPercentage => false, css.css_properties.border_image.BorderImageSideWidth => true, *const css.css_values.percentage.DimensionPercentage(css.css_values.length.LengthValue) => true, + CssColor => true, + css.css_properties.border.LineStyle => false, + css.css_properties.border.BorderSideWidth => true, + css.css_values.length.LengthPercentageOrAuto => true, else => @compileError("Don't know if " ++ @typeName(T) ++ " needs deinit. Please add it to this switch statement."), }; } @@ -77,6 +81,15 @@ pub fn Rect(comptime T: type) type { }; } + pub fn all(val: T) This { + return This{ + .top = val, + .right = val, + .bottom = val, + .left = val, + }; + } + pub fn deinit(this: *const This, allocator: std.mem.Allocator) void { if (comptime needs_deinit) { this.top.deinit(allocator); diff --git a/src/css/values/resolution.zig b/src/css/values/resolution.zig index 8201eb49b2..951b809b21 100644 --- a/src/css/values/resolution.zig +++ b/src/css/values/resolution.zig @@ -34,6 +34,27 @@ pub const Resolution = union(enum) { // ~toCssImpl const This = @This(); + pub fn hash(this: *const @This(), hasher: *std.hash.Wyhash) void { + return css.implementHash(@This(), this, hasher); + } + + pub fn eql(this: *const Resolution, other: *const Resolution) bool { + return switch (this.*) { + .dpi => |*a| switch (other.*) { + .dpi => a.* == other.dpi, + else => false, + }, + .dpcm => |*a| switch (other.*) { + .dpcm => a.* == other.dpcm, + else => false, + }, + .dppx => |*a| switch (other.*) { + .dppx => a.* == other.dppx, + else => false, + }, + }; + } + pub fn parse(input: *css.Parser) Result(Resolution) { // TODO: calc? const location = input.currentSourceLocation(); diff --git a/src/css/values/size.zig b/src/css/values/size.zig index 98f5e7f3a4..07aceaa9aa 100644 --- a/src/css/values/size.zig +++ b/src/css/values/size.zig @@ -67,6 +67,10 @@ pub fn Size2D(comptime T: type) type { }; } + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } + pub inline fn valEql(lhs: *const T, rhs: *const T) bool { return switch (T) { f32 => lhs.* == rhs.*, diff --git a/src/css/values/syntax.zig b/src/css/values/syntax.zig index f01c0fbe51..5f8a743367 100644 --- a/src/css/values/syntax.zig +++ b/src/css/values/syntax.zig @@ -37,6 +37,10 @@ pub const SyntaxString = union(enum) { const This = @This(); + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } + pub fn toCss(this: *const This, comptime W: type, dest: *Printer(W)) PrintErr!void { try dest.writeChar('"'); switch (this.*) { @@ -291,6 +295,10 @@ pub const SyntaxComponent = struct { .space => dest.writeChar('+'), }; } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } }; /// A [syntax component component name](https://drafts.css-houdini.org/css-properties-values-api/#supported-names). @@ -411,6 +419,10 @@ pub const SyntaxComponentKind = union(enum) { // https://drafts.csswg.org/css-syntax-3/#ident-code-point return isIdentStart(c) or c >= '0' and c <= '9' or c == '-'; } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } }; pub const ParsedComponent = union(enum) { @@ -450,6 +462,10 @@ pub const ParsedComponent = union(enum) { components: ArrayList(ParsedComponent), /// A multiplier describing how the components repeat. multiplier: Multiplier, + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } }, /// A raw token stream. token_list: css.css_properties.custom.TokenList, @@ -491,6 +507,10 @@ pub const ParsedComponent = union(enum) { .token_list => |*t| try t.toCss(W, dest, false), }; } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } }; /// A [multiplier](https://drafts.css-houdini.org/css-properties-values-api/#multipliers) for a diff --git a/src/css/values/time.zig b/src/css/values/time.zig index 976edac733..23ed5c9f86 100644 --- a/src/css/values/time.zig +++ b/src/css/values/time.zig @@ -36,6 +36,13 @@ pub const Time = union(enum) { const Tag = enum(u8) { seconds = 1, milliseconds = 2 }; + pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { + return css.implementEql(@This(), lhs, rhs); + } + pub fn hash(this: *const @This(), hasher: *std.hash.Wyhash) void { + return css.implementHash(@This(), this, hasher); + } + pub fn parse(input: *css.Parser) Result(Time) { var calc_result = switch (input.tryParse(Calc(Time).parse, .{})) { .result => |v| v, diff --git a/src/css/values/url.zig b/src/css/values/url.zig index 1bf45f5694..ffa9bea03d 100644 --- a/src/css/values/url.zig +++ b/src/css/values/url.zig @@ -144,4 +144,20 @@ pub const Url = struct { try dest.writeChar(')'); } } + + pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { + return css.implementDeepClone(@This(), this, allocator); + } + + // TODO: dedupe import records?? + // This might not fucking work + pub fn eql(this: *const Url, other: *const Url) bool { + return this.import_record_idx == other.import_record_idx; + } + + // TODO: dedupe import records?? + // This might not fucking work + pub fn hash(this: *const @This(), hasher: *std.hash.Wyhash) void { + return css.implementHash(@This(), this, hasher); + } }; diff --git a/src/js_ast.zig b/src/js_ast.zig index f3c4bc578f..2c325f6d1c 100644 --- a/src/js_ast.zig +++ b/src/js_ast.zig @@ -7053,7 +7053,7 @@ pub const BundledAst = struct { hashbang: string = "", parts: Part.List = .{}, css: ?*bun.css.BundlerStyleSheet = null, - url_for_css: []const u8 = "", + url_for_css: ?[]const u8 = null, symbols: Symbol.List = .{}, module_scope: Scope = .{}, char_freq: CharFreq = undefined, @@ -7215,11 +7215,25 @@ pub const BundledAst = struct { } /// TODO: I don't like having to do this extra allocation. Is there a way to only do this if we know it is imported by a CSS file? - pub fn addUrlForCss(this: *BundledAst, allocator: std.mem.Allocator, css_enabled: bool, source: *const logger.Source, mime_type_: ?[]const u8) void { + pub fn addUrlForCss( + this: *BundledAst, + allocator: std.mem.Allocator, + css_enabled: bool, + source: *const logger.Source, + mime_type_: ?[]const u8, + unique_key: ?[]const u8, + ) void { if (css_enabled) { const mime_type = if (mime_type_) |m| m else MimeType.byExtension(bun.strings.trimLeadingChar(std.fs.path.extension(source.key_path.text), '.')).value; const contents = source.contents; + // TODO: make this configurable + const COPY_THRESHOLD = 128 * 1024; // 128kb + const should_copy = contents.len >= COPY_THRESHOLD and unique_key != null; this.url_for_css = url_for_css: { + // Copy it + if (should_copy) break :url_for_css unique_key.?; + + // Encode as base64 const encode_len = bun.base64.encodeLen(contents); if (encode_len == 0) return; const data_url_prefix_len = "data:".len + mime_type.len + ";base64,".len; diff --git a/src/linker.zig b/src/linker.zig index f9436f4d9f..1a6cb7ec1a 100644 --- a/src/linker.zig +++ b/src/linker.zig @@ -281,6 +281,10 @@ pub const Linker = struct { continue; } + if (strings.hasSuffixComptime(import_record.path.text, ".css")) { + import_record.tag = .css; + } + // Resolve dynamic imports lazily for perf if (import_record.kind == .dynamic) { continue; diff --git a/src/meta.zig b/src/meta.zig index 569a6d368c..23aac95ed2 100644 --- a/src/meta.zig +++ b/src/meta.zig @@ -190,3 +190,127 @@ fn CreateUniqueTuple(comptime N: comptime_int, comptime types: [N]type) type { }, }); } + +pub fn hasStableMemoryLayout(comptime T: type) bool { + const tyinfo = @typeInfo(T); + return switch (tyinfo) { + .Type => true, + .Void => true, + .Bool => true, + .Int => true, + .Float => true, + .Enum => { + // not supporting this rn + if (tyinfo.Enum.is_exhaustive) return false; + return hasStableMemoryLayout(tyinfo.Enum.tag_type); + }, + .Struct => switch (tyinfo.Struct.layout) { + .auto => { + inline for (tyinfo.Struct.fields) |field| { + if (!hasStableMemoryLayout(field.field_type)) return false; + } + return true; + }, + .@"extern" => true, + .@"packed" => false, + }, + .Union => switch (tyinfo.Union.layout) { + .auto => { + if (tyinfo.Union.tag_type == null or !hasStableMemoryLayout(tyinfo.Union.tag_type.?)) return false; + + inline for (tyinfo.Union.fields) |field| { + if (!hasStableMemoryLayout(field.type)) return false; + } + + return true; + }, + .@"extern" => true, + .@"packed" => false, + }, + else => true, + }; +} + +pub fn isSimpleCopyType(comptime T: type) bool { + const tyinfo = @typeInfo(T); + return switch (tyinfo) { + .Void => true, + .Bool => true, + .Int => true, + .Float => true, + .Enum => true, + .Struct => { + inline for (tyinfo.Struct.fields) |field| { + if (!isSimpleCopyType(field.type)) return false; + } + return true; + }, + .Union => { + inline for (tyinfo.Union.fields) |field| { + if (!isSimpleCopyType(field.type)) return false; + } + return true; + }, + .Optional => return isSimpleCopyType(tyinfo.Optional.child), + else => false, + }; +} + +pub fn isScalar(comptime T: type) bool { + return switch (T) { + i32, u32, i64, u64, f32, f64, bool => true, + else => { + const tyinfo = @typeInfo(T); + if (tyinfo == .Enum) return true; + return false; + }, + }; +} + +pub fn isSimpleEqlType(comptime T: type) bool { + const tyinfo = @typeInfo(T); + return switch (tyinfo) { + .Type => true, + .Void => true, + .Bool => true, + .Int => true, + .Float => true, + .Enum => true, + else => false, + }; +} + +pub const ListContainerType = enum { + array_list, + baby_list, + small_list, +}; +pub fn looksLikeListContainerType(comptime T: type) ?struct { list: ListContainerType, child: type } { + const tyinfo = @typeInfo(T); + if (tyinfo == .Struct) { + // Looks like array list + if (tyinfo.Struct.fields.len == 2 and + std.mem.eql(u8, tyinfo.Struct.fields[0].name, "items") and + std.mem.eql(u8, tyinfo.Struct.fields[1].name, "capacity")) + return .{ .list = .array_list, .child = std.meta.Child(tyinfo.Struct.fields[0].type) }; + + // Looks like babylist + if (tyinfo.Struct.fields.len == 3 and + std.mem.eql(u8, tyinfo.Struct.fields[0].name, "ptr") and + std.mem.eql(u8, tyinfo.Struct.fields[1].name, "len") and + std.mem.eql(u8, tyinfo.Struct.fields[2].name, "cap")) + return .{ .list = .baby_list, .child = std.meta.Child(tyinfo.Struct.fields[0].type) }; + + // Looks like SmallList + if (tyinfo.Struct.fields.len == 2 and + std.mem.eql(u8, tyinfo.Struct.fields[0].name, "capacity") and + std.mem.eql(u8, tyinfo.Struct.fields[1].name, "data")) return .{ + .list = .small_list, + .child = std.meta.Child( + @typeInfo(tyinfo.Struct.fields[1].type).Union.fields[0].type, + ), + }; + } + + return null; +} diff --git a/test/bundler/esbuild/css.test.ts b/test/bundler/esbuild/css.test.ts index 7e61e61cf0..eb5b10722e 100644 --- a/test/bundler/esbuild/css.test.ts +++ b/test/bundler/esbuild/css.test.ts @@ -21,12 +21,46 @@ describe('bundler', () => { api.expectFile('/out.js').toEqualIgnoringWhitespace(` /* entry.css */ body { - background: white; + background: #fff; color: #000; }`) }, }); + itBundled("css/CSSEntryPointEmpty", { + experimentalCss: true, + files: { + "/entry.css": /* css */ `\n`, + }, + outfile: '/out.js', + onAfterBundle(api) { + api.expectFile('/out.js').toEqualIgnoringWhitespace(` +/* entry.css */`) + }, + }); + + itBundled("css/CSSNesting", { + experimentalCss: true, + files: { + "/entry.css": /* css */ ` +body { + h1 { + color: white; + } +}`, + }, + outfile: '/out.js', + onAfterBundle(api) { + api.expectFile('/out.js').toEqualIgnoringWhitespace(` +/* entry.css */ +body { + &h1 { + color: #fff; + } +} +`) + }, + }); itBundled("css/CSSAtImportMissing", { experimentalCss: true,