mirror of
https://github.com/oven-sh/bun
synced 2026-02-09 18:38:55 +00:00
308 lines
11 KiB
Zig
308 lines
11 KiB
Zig
const std = @import("std");
|
|
const Allocator = std.mem.Allocator;
|
|
const bun = @import("root").bun;
|
|
const logger = bun.logger;
|
|
const Log = logger.Log;
|
|
|
|
pub const css = @import("./css_parser.zig");
|
|
|
|
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;
|
|
|
|
const UnparsedProperty = css.css_properties.custom.UnparsedProperty;
|
|
|
|
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 {
|
|
none,
|
|
style_rule,
|
|
keyframes,
|
|
style_attribute,
|
|
};
|
|
|
|
pub const PropertyHandlerContext = struct {
|
|
allocator: Allocator,
|
|
targets: css.targets.Targets,
|
|
is_important: bool,
|
|
supports: ArrayList(SupportsEntry),
|
|
ltr: ArrayList(css.Property),
|
|
rtl: ArrayList(css.Property),
|
|
dark: ArrayList(css.Property),
|
|
context: DeclarationContext,
|
|
unused_symbols: *const std.StringArrayHashMapUnmanaged(void),
|
|
|
|
pub fn new(
|
|
allocator: Allocator,
|
|
targets: css.targets.Targets,
|
|
unused_symbols: *const std.StringArrayHashMapUnmanaged(void),
|
|
) PropertyHandlerContext {
|
|
return PropertyHandlerContext{
|
|
.allocator = allocator,
|
|
.targets = targets,
|
|
.is_important = false,
|
|
.supports = ArrayList(SupportsEntry){},
|
|
.ltr = ArrayList(css.Property){},
|
|
.rtl = ArrayList(css.Property){},
|
|
.dark = ArrayList(css.Property){},
|
|
.context = DeclarationContext.none,
|
|
.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 addLogicalRule(this: *@This(), allocator: Allocator, ltr: css.Property, rtl: css.Property) void {
|
|
this.ltr.append(allocator, ltr) catch unreachable;
|
|
this.rtl.append(allocator, rtl) catch unreachable;
|
|
}
|
|
|
|
pub fn shouldCompileLogical(this: *const @This(), feature: css.compat.Feature) bool {
|
|
// Don't convert logical properties in style attributes because
|
|
// our fallbacks rely on extra rules to define --ltr and --rtl.
|
|
if (this.context == DeclarationContext.style_attribute) return false;
|
|
|
|
return this.targets.shouldCompileLogical(feature);
|
|
}
|
|
|
|
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();
|
|
}
|
|
|
|
pub fn addConditionalProperty(this: *@This(), condition: css.SupportsCondition, property: css.Property) void {
|
|
if (this.context != DeclarationContext.style_rule) return;
|
|
|
|
if (brk: {
|
|
for (this.supports.items) |*supp| {
|
|
if (condition.eql(&supp.condition)) break :brk supp;
|
|
}
|
|
break :brk null;
|
|
}) |entry| {
|
|
if (this.is_important) {
|
|
entry.important_declarations.append(this.allocator, property) catch bun.outOfMemory();
|
|
} else {
|
|
entry.declarations.append(this.allocator, property) catch bun.outOfMemory();
|
|
}
|
|
} else {
|
|
var important_declarations = ArrayList(css.Property){};
|
|
var declarations = ArrayList(css.Property){};
|
|
if (this.is_important) {
|
|
important_declarations.append(this.allocator, property) catch bun.outOfMemory();
|
|
} else {
|
|
declarations.append(this.allocator, property) catch bun.outOfMemory();
|
|
}
|
|
this.supports.append(this.allocator, SupportsEntry{
|
|
.condition = condition,
|
|
.declarations = declarations,
|
|
.important_declarations = important_declarations,
|
|
}) catch bun.outOfMemory();
|
|
}
|
|
}
|
|
|
|
pub fn addUnparsedFallbacks(this: *@This(), unparsed: *UnparsedProperty) void {
|
|
if (this.context != DeclarationContext.style_rule and this.context != DeclarationContext.style_attribute) {
|
|
return;
|
|
}
|
|
|
|
const fallbacks = unparsed.value.getFallbacks(this.allocator, this.targets);
|
|
|
|
for (fallbacks.slice()) |condition_and_fallback| {
|
|
this.addConditionalProperty(condition_and_fallback[0], css.Property{
|
|
.unparsed = UnparsedProperty{
|
|
.property_id = unparsed.property_id.deepClone(this.allocator),
|
|
.value = condition_and_fallback[1],
|
|
},
|
|
});
|
|
}
|
|
}
|
|
};
|