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

283 lines
9.6 KiB
Zig

const bun = @import("root").bun;
const std = @import("std");
const builtin = @import("builtin");
const Arena = @import("../mimalloc_arena.zig").Arena;
const Allocator = std.mem.Allocator;
const ArrayList = std.ArrayList;
const JSC = bun.JSC;
const JSValue = bun.JSC.JSValue;
const JSPromise = bun.JSC.JSPromise;
const JSGlobalObject = bun.JSC.JSGlobalObject;
threadlocal var arena_: ?Arena = null;
const TestKind = enum {
normal,
minify,
prefix,
};
pub fn minifyTestWithOptions(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) JSC.JSValue {
return testingImpl(globalThis, callframe, .minify);
}
pub fn prefixTestWithOptions(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) JSC.JSValue {
return testingImpl(globalThis, callframe, .prefix);
}
pub fn testWithOptions(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) JSC.JSValue {
return testingImpl(globalThis, callframe, .normal);
}
pub fn testingImpl(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, comptime test_kind: TestKind) JSC.JSValue {
var arena = arena_ orelse brk: {
break :brk Arena.init() catch @panic("oopsie arena no good");
};
defer arena.reset();
const alloc = arena.allocator();
const arguments_ = callframe.arguments(2);
var arguments = JSC.Node.ArgumentsSlice.init(globalThis.bunVM(), arguments_.slice());
const source_arg: JSC.JSValue = arguments.nextEat() orelse {
globalThis.throw("minifyTestWithOptions: expected 2 arguments, got 0", .{});
return .undefined;
};
if (!source_arg.isString()) {
globalThis.throw("minifyTestWithOptions: expected source to be a string", .{});
return .undefined;
}
const source_bunstr = source_arg.toBunString(globalThis);
defer source_bunstr.deref();
const source = source_bunstr.toUTF8(bun.default_allocator);
defer source.deinit();
const expected_arg = arguments.nextEat() orelse {
globalThis.throw("minifyTestWithOptions: expected 2 arguments, got 1", .{});
return .undefined;
};
if (!expected_arg.isString()) {
globalThis.throw("minifyTestWithOptions: expected `expected` arg to be a string", .{});
return .undefined;
}
const expected_bunstr = expected_arg.toBunString(globalThis);
defer expected_bunstr.deref();
const expected = expected_bunstr.toUTF8(bun.default_allocator);
defer expected.deinit();
const options_arg = arguments.nextEat();
var log = bun.logger.Log.init(alloc);
defer log.deinit();
const parser_options = parser_options: {
const opts = bun.css.ParserOptions.default(alloc, &log);
if (test_kind == .prefix) break :parser_options opts;
if (options_arg) |optargs| {
_ = optargs; // autofix
// if (optargs.isObject()) {
// if (optargs.getStr
// }
std.debug.panic("ZACK: suppor this lol", .{});
}
break :parser_options opts;
};
var import_records = bun.BabyList(bun.ImportRecord){};
switch (bun.css.StyleSheet(bun.css.DefaultAtRule).parse(
alloc,
source.slice(),
parser_options,
&import_records,
)) {
.result => |stylesheet_| {
var stylesheet = stylesheet_;
var minify_options: bun.css.MinifyOptions = bun.css.MinifyOptions.default();
switch (test_kind) {
.minify => {},
.normal => {},
.prefix => {
if (options_arg) |optarg| {
if (optarg.isObject()) {
minify_options.targets.browsers = targetsFromJS(globalThis, optarg);
}
}
},
}
_ = stylesheet.minify(alloc, minify_options).assert();
const result = stylesheet.toCss(alloc, bun.css.PrinterOptions{
.minify = switch (test_kind) {
.minify => true,
.normal => false,
.prefix => false,
},
}, &import_records) catch |e| {
bun.handleErrorReturnTrace(e, @errorReturnTrace());
return .undefined;
};
return bun.String.fromBytes(result.code).toJS(globalThis);
},
.err => |err| {
if (log.hasAny()) {
return log.toJS(globalThis, bun.default_allocator, "parsing failed:");
}
globalThis.throw("parsing failed: {}", .{err.kind});
return .undefined;
},
}
}
fn targetsFromJS(globalThis: *JSC.JSGlobalObject, jsobj: JSValue) bun.css.targets.Browsers {
var targets = bun.css.targets.Browsers{};
if (jsobj.getTruthy(globalThis, "android")) |val| {
if (val.isInt32()) {
if (val.getNumber()) |value| {
targets.android = @intFromFloat(value);
}
}
}
if (jsobj.getTruthy(globalThis, "chrome")) |val| {
if (val.isInt32()) {
if (val.getNumber()) |value| {
targets.chrome = @intFromFloat(value);
}
}
}
if (jsobj.getTruthy(globalThis, "edge")) |val| {
if (val.isInt32()) {
if (val.getNumber()) |value| {
targets.edge = @intFromFloat(value);
}
}
}
if (jsobj.getTruthy(globalThis, "firefox")) |val| {
if (val.isInt32()) {
if (val.getNumber()) |value| {
targets.firefox = @intFromFloat(value);
}
}
}
if (jsobj.getTruthy(globalThis, "ie")) |val| {
if (val.isInt32()) {
if (val.getNumber()) |value| {
targets.ie = @intFromFloat(value);
}
}
}
if (jsobj.getTruthy(globalThis, "ios_saf")) |val| {
if (val.isInt32()) {
if (val.getNumber()) |value| {
targets.ios_saf = @intFromFloat(value);
}
}
}
if (jsobj.getTruthy(globalThis, "opera")) |val| {
if (val.isInt32()) {
if (val.getNumber()) |value| {
targets.opera = @intFromFloat(value);
}
}
}
if (jsobj.getTruthy(globalThis, "safari")) |val| {
if (val.isInt32()) {
if (val.getNumber()) |value| {
targets.safari = @intFromFloat(value);
}
}
}
if (jsobj.getTruthy(globalThis, "samsung")) |val| {
if (val.isInt32()) {
if (val.getNumber()) |value| {
targets.samsung = @intFromFloat(value);
}
}
}
return targets;
}
pub fn attrTest(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) JSC.JSValue {
var arena = arena_ orelse brk: {
break :brk Arena.init() catch @panic("oopsie arena no good");
};
defer arena.reset();
const alloc = arena.allocator();
const arguments_ = callframe.arguments(4);
var arguments = JSC.Node.ArgumentsSlice.init(globalThis.bunVM(), arguments_.slice());
const source_arg: JSC.JSValue = arguments.nextEat() orelse {
globalThis.throw("attrTest: expected 3 arguments, got 0", .{});
return .undefined;
};
if (!source_arg.isString()) {
globalThis.throw("attrTest: expected source to be a string", .{});
return .undefined;
}
const source_bunstr = source_arg.toBunString(globalThis);
defer source_bunstr.deref();
const source = source_bunstr.toUTF8(bun.default_allocator);
defer source.deinit();
const expected_arg = arguments.nextEat() orelse {
globalThis.throw("attrTest: expected 3 arguments, got 1", .{});
return .undefined;
};
if (!expected_arg.isString()) {
globalThis.throw("attrTest: expected `expected` arg to be a string", .{});
return .undefined;
}
const expected_bunstr = expected_arg.toBunString(globalThis);
defer expected_bunstr.deref();
const expected = expected_bunstr.toUTF8(bun.default_allocator);
defer expected.deinit();
const minify_arg: JSC.JSValue = arguments.nextEat() orelse {
globalThis.throw("attrTest: expected 3 arguments, got 2", .{});
return .undefined;
};
const minify = minify_arg.isBoolean() and minify_arg.toBoolean();
var targets: bun.css.targets.Targets = .{};
if (arguments.nextEat()) |arg| {
if (arg.isObject()) {
targets.browsers = targetsFromJS(globalThis, arg);
}
}
var log = bun.logger.Log.init(alloc);
defer log.deinit();
const parser_options = bun.css.ParserOptions.default(alloc, &log);
var import_records = bun.BabyList(bun.ImportRecord){};
switch (bun.css.StyleAttribute.parse(alloc, source.slice(), parser_options, &import_records)) {
.result => |stylesheet_| {
var stylesheet = stylesheet_;
var minify_options: bun.css.MinifyOptions = bun.css.MinifyOptions.default();
minify_options.targets = targets;
stylesheet.minify(alloc, minify_options);
const result = stylesheet.toCss(alloc, bun.css.PrinterOptions{
.minify = minify,
.targets = targets,
}, &import_records) catch |e| {
bun.handleErrorReturnTrace(e, @errorReturnTrace());
return .undefined;
};
return bun.String.fromBytes(result.code).toJS(globalThis);
},
.err => |err| {
if (log.hasAny()) {
return log.toJS(globalThis, bun.default_allocator, "parsing failed:");
}
globalThis.throw("parsing failed: {}", .{err.kind});
return .undefined;
},
}
}