Files
bun.sh/src/semver/SemverObject.zig
2025-07-23 17:10:58 -07:00

140 lines
4.2 KiB
Zig

const SemverObject = @This();
pub fn create(globalThis: *jsc.JSGlobalObject) jsc.JSValue {
const object = jsc.JSValue.createEmptyObject(globalThis, 2);
object.put(
globalThis,
jsc.ZigString.static("satisfies"),
jsc.host_fn.NewFunction(
globalThis,
jsc.ZigString.static("satisfies"),
2,
SemverObject.satisfies,
false,
),
);
object.put(
globalThis,
jsc.ZigString.static("order"),
jsc.host_fn.NewFunction(
globalThis,
jsc.ZigString.static("order"),
2,
SemverObject.order,
false,
),
);
return object;
}
pub fn order(
globalThis: *jsc.JSGlobalObject,
callFrame: *jsc.CallFrame,
) bun.JSError!jsc.JSValue {
var arena = std.heap.ArenaAllocator.init(bun.default_allocator);
defer arena.deinit();
var stack_fallback = std.heap.stackFallback(512, arena.allocator());
const allocator = stack_fallback.get();
const arguments = callFrame.arguments_old(2).slice();
if (arguments.len < 2) {
return globalThis.throw("Expected two arguments", .{});
}
const left_arg = arguments[0];
const right_arg = arguments[1];
const left_string = try left_arg.toJSString(globalThis);
const right_string = try right_arg.toJSString(globalThis);
const left = left_string.toSlice(globalThis, allocator);
defer left.deinit();
const right = right_string.toSlice(globalThis, allocator);
defer right.deinit();
if (!strings.isAllASCII(left.slice())) return .jsNumber(0);
if (!strings.isAllASCII(right.slice())) return .jsNumber(0);
const left_result = Version.parse(SlicedString.init(left.slice(), left.slice()));
const right_result = Version.parse(SlicedString.init(right.slice(), right.slice()));
if (!left_result.valid) {
return globalThis.throw("Invalid SemVer: {s}\n", .{left.slice()});
}
if (!right_result.valid) {
return globalThis.throw("Invalid SemVer: {s}\n", .{right.slice()});
}
const left_version = left_result.version.max();
const right_version = right_result.version.max();
return switch (left_version.orderWithoutBuild(right_version, left.slice(), right.slice())) {
.eq => .jsNumber(0),
.gt => .jsNumber(1),
.lt => .jsNumber(-1),
};
}
pub fn satisfies(globalThis: *jsc.JSGlobalObject, callFrame: *jsc.CallFrame) bun.JSError!jsc.JSValue {
var arena = std.heap.ArenaAllocator.init(bun.default_allocator);
defer arena.deinit();
var stack_fallback = std.heap.stackFallback(512, arena.allocator());
const allocator = stack_fallback.get();
const arguments = callFrame.arguments_old(2).slice();
if (arguments.len < 2) {
return globalThis.throw("Expected two arguments", .{});
}
const left_arg = arguments[0];
const right_arg = arguments[1];
const left_string = try left_arg.toJSString(globalThis);
const right_string = try right_arg.toJSString(globalThis);
const left = left_string.toSlice(globalThis, allocator);
defer left.deinit();
const right = right_string.toSlice(globalThis, allocator);
defer right.deinit();
if (!strings.isAllASCII(left.slice())) return .false;
if (!strings.isAllASCII(right.slice())) return .false;
const left_result = Version.parse(SlicedString.init(left.slice(), left.slice()));
if (left_result.wildcard != .none) {
return .false;
}
const left_version = left_result.version.min();
const right_group = try Query.parse(
allocator,
right.slice(),
SlicedString.init(right.slice(), right.slice()),
);
defer right_group.deinit();
const right_version = right_group.getExactVersion();
if (right_version != null) {
return .jsBoolean(left_version.eql(right_version.?));
}
return .jsBoolean(right_group.satisfies(left_version, right.slice(), left.slice()));
}
const std = @import("std");
const bun = @import("bun");
const default_allocator = bun.default_allocator;
const jsc = bun.jsc;
const strings = bun.strings;
const Query = bun.Semver.Query;
const SlicedString = bun.Semver.SlicedString;
const Version = bun.Semver.Version;