mirror of
https://github.com/ziex-dev/ziex.git
synced 2026-07-20 02:29:36 -06:00
feat: sync generated types
This commit is contained in:
parent
ac90841866
commit
991fda56c1
133
site/app/pages/examples/event/page.zx
Normal file
133
site/app/pages/examples/event/page.zx
Normal file
@ -0,0 +1,133 @@
|
||||
// Demonstrates and exercises `zx.client.Event.as(T)` across every case the
|
||||
// debug-build type assertion guards:
|
||||
//
|
||||
// 1. Correct match — as(PointerEvent) in a click handler → reads data
|
||||
// 2. Correct match — as(KeyboardEvent) in a keydown handler → reads data
|
||||
// 3. Correct match — as(InputEvent) in an input handler → reads data
|
||||
// 4. Correct match — as(SubmitEvent) in a submit handler → reads data
|
||||
// 5. Catch-all — as(Event) in any handler → never asserts
|
||||
// 6. Deliberate mismatch — as(KeyboardEvent) in a click handler → debug panic
|
||||
//
|
||||
// Open the browser console and interact with each control to watch the typed
|
||||
// fields print. In a debug build, clicking "Trigger type mismatch" panics with
|
||||
// a descriptive message — that's the guard working.
|
||||
|
||||
pub fn Page(ctx: zx.PageContext) zx.Component {
|
||||
return (
|
||||
<main @allocator={ctx.arena}>
|
||||
<h1>Event.as() type-safety demo</h1>
|
||||
<p>Open the console, then interact with each control below.</p>
|
||||
<EventDemo @rendering={.client} />
|
||||
</main>
|
||||
);
|
||||
}
|
||||
|
||||
pub fn EventDemo(ctx: *zx.ComponentContext) zx.Component {
|
||||
return (
|
||||
<div @allocator={ctx.allocator}>
|
||||
<section>
|
||||
<p>1. PointerEvent (click)</p>
|
||||
<button onclick={ctx.bind(onClick)}>Click me</button>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<p>2. KeyboardEvent (keydown) + 3. InputEvent (input)</p>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Type here"
|
||||
onkeydown={ctx.bind(onKeyDown)}
|
||||
oninput={ctx.bind(onInput)}
|
||||
/>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<p>4. SubmitEvent (submit)</p>
|
||||
<form onsubmit={ctx.bind(onSubmit)}>
|
||||
<button type="submit">Submit form</button>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<p>5. Catch-all Event (any type is valid)</p>
|
||||
<button onclick={ctx.bind(onAnyEvent)}>Read as generic Event</button>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<p>6. Deliberate mismatch (debug build panics)</p>
|
||||
<button onclick={ctx.bind(onMismatch)}>Trigger type mismatch</button>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// 1. Correct: click events are backed by PointerEvent (a superset of MouseEvent).
|
||||
fn onClick(ctx: *zx.client.Event.Stateful) void {
|
||||
const e = ctx.as(zx.client.events.PointerEvent, zx.allocator);
|
||||
zx.log.info("PointerEvent: type={s} at ({d},{d}) button={d} pointerType={s} on <{s}>", .{
|
||||
e.type,
|
||||
e.client_x,
|
||||
e.client_y,
|
||||
e.button,
|
||||
e.pointer_type,
|
||||
if (e.target) |t| t.tag_name else "unknown",
|
||||
});
|
||||
}
|
||||
|
||||
// 2. Correct: keydown events are backed by KeyboardEvent.
|
||||
fn onKeyDown(ctx: *zx.client.Event.Stateful) void {
|
||||
const e = ctx.as(zx.client.events.KeyboardEvent, zx.allocator);
|
||||
zx.log.info("KeyboardEvent: key={s} code={s} ctrl={any} shift={any} repeat={any}", .{
|
||||
e.key,
|
||||
e.code,
|
||||
e.ctrl_key,
|
||||
e.shift_key,
|
||||
e.repeat,
|
||||
});
|
||||
}
|
||||
|
||||
// 3. Correct: input events are backed by InputEvent.
|
||||
fn onInput(ctx: *zx.client.Event.Stateful) void {
|
||||
const e = ctx.as(zx.client.events.InputEvent, zx.allocator);
|
||||
zx.log.info("InputEvent: type={s} inputType={s} data={s} value={s}", .{
|
||||
e.type,
|
||||
e.input_type,
|
||||
e.data orelse "",
|
||||
ctx.value() orelse "",
|
||||
});
|
||||
}
|
||||
|
||||
// 4. Correct: submit events are backed by SubmitEvent.
|
||||
fn onSubmit(ctx: *zx.client.Event.Stateful) void {
|
||||
ctx.preventDefault();
|
||||
const e = ctx.as(zx.client.events.SubmitEvent, zx.allocator);
|
||||
zx.log.info("SubmitEvent: type={s} bubbles={any} cancelable={any}", .{
|
||||
e.type,
|
||||
e.bubbles,
|
||||
e.cancelable,
|
||||
});
|
||||
}
|
||||
|
||||
// 5. Catch-all: `Event` is valid for every event type, so the guard is skipped.
|
||||
fn onAnyEvent(ctx: *zx.client.Event.Stateful) void {
|
||||
const e = ctx.as(zx.client.events.Event, zx.allocator);
|
||||
zx.log.info("Event (catch-all): type={s} bubbles={any} isTrusted={any}", .{
|
||||
e.type,
|
||||
e.bubbles,
|
||||
e.is_trusted,
|
||||
});
|
||||
}
|
||||
|
||||
// 6. Mismatch: a click is NOT a KeyboardEvent. In a debug build the assertion
|
||||
// in `as()` panics with a message naming the requested vs. actual type. This is
|
||||
// the bug the guard exists to catch.
|
||||
fn onMismatch(ctx: *zx.client.Event.Stateful) void {
|
||||
zx.log.info("About to request KeyboardEvent for a click — expect a debug panic.", .{});
|
||||
const e = ctx.as(zx.client.events.KeyboardEvent, zx.allocator);
|
||||
zx.log.info("Unreachable in debug builds: {s}", .{e.key});
|
||||
}
|
||||
|
||||
pub const options: zx.PageOptions = .{
|
||||
.methods = &.{ .GET, .POST },
|
||||
};
|
||||
|
||||
const zx = @import("zx");
|
||||
@ -1,16 +1,18 @@
|
||||
const Event = @This();
|
||||
|
||||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
|
||||
const zx = @import("../../root.zig");
|
||||
const client = @import("window.zig");
|
||||
const generated_events = @import("events/generated.zig");
|
||||
|
||||
const js = zx.client.js;
|
||||
const reactivity = client.reactivity;
|
||||
|
||||
const Allocator = zx.Allocator;
|
||||
pub const Kind = generated_events.Kind;
|
||||
const platform_role = zx.platform.role;
|
||||
|
||||
const Event = @This();
|
||||
|
||||
_internal: Internal = .{},
|
||||
|
||||
pub const Internal = struct {
|
||||
@ -58,7 +60,35 @@ pub fn key(self: Event) ?[]const u8 {
|
||||
/// Get the event data by providing zx.client.events.<Type>.
|
||||
pub fn as(self: Event, comptime T: type, allocator: Allocator) T {
|
||||
if (platform_role != .client) return std.mem.zeroInit(T, .{});
|
||||
return readStruct(T, allocator, self.getEvent().ref);
|
||||
const event = self.getEvent();
|
||||
if (comptime builtin.mode == .Debug) assertType(T, event);
|
||||
return readStruct(T, allocator, event.ref);
|
||||
}
|
||||
|
||||
/// Debug-only guard: validates that requested struct `T` matches the live event.
|
||||
inline fn assertType(comptime T: type, event: client.Event) void {
|
||||
// The bare `Event` interface is the catch-all — valid for every event.
|
||||
if (comptime T == generated_events.Event) return;
|
||||
|
||||
const type_str = event.getType(zx.allocator) orelse return;
|
||||
defer zx.allocator.free(type_str);
|
||||
|
||||
const kind = generated_events.kindForType(type_str) orelse return; // custom event
|
||||
switch (kind) {
|
||||
inline else => |k| {
|
||||
if (generated_events.Data(k) != T) {
|
||||
std.log.err(
|
||||
\\Event.as, incorrect type requested for '{s}' event
|
||||
\\
|
||||
\\ Requested: {s}
|
||||
\\ Expected: {s}
|
||||
\\
|
||||
,
|
||||
.{ type_str, @typeName(T), @typeName(generated_events.Data(k)) },
|
||||
);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn data(self: Event, comptime kind: Kind, allocator: Allocator) Data(kind) {
|
||||
|
||||
@ -1736,6 +1736,7 @@ pub const WheelEvent = struct {
|
||||
delta_y: f64 = 0,
|
||||
delta_z: f64 = 0,
|
||||
delta_mode: u32 = 0,
|
||||
momentum: bool = false,
|
||||
page_x: f64 = 0,
|
||||
page_y: f64 = 0,
|
||||
x: f64 = 0,
|
||||
@ -2191,6 +2192,7 @@ pub const events = struct {
|
||||
pub const connect = Event;
|
||||
pub const readingerror = Event;
|
||||
pub const selectionchange = Event;
|
||||
pub const toolchange = Event;
|
||||
pub const open = Event;
|
||||
pub const sinkchange = Event;
|
||||
pub const beforetoggle = ToggleEvent;
|
||||
@ -2249,6 +2251,7 @@ pub const events = struct {
|
||||
pub const freeze = Event;
|
||||
pub const notificationclose = NotificationEvent;
|
||||
pub const gamepadconnected = GamepadEvent;
|
||||
pub const quotaoverflow = Event;
|
||||
pub const pointermove = PointerEvent;
|
||||
pub const mouseover = MouseEvent;
|
||||
pub const cuechange = Event;
|
||||
@ -2288,7 +2291,7 @@ pub const events = struct {
|
||||
pub const remove = AnimationPlaybackEvent;
|
||||
pub const fetch = FetchEvent;
|
||||
pub const screenschange = Event;
|
||||
pub const blur = Event;
|
||||
pub const blur = FocusEvent;
|
||||
pub const pointerenter = PointerEvent;
|
||||
pub const characterboundsupdate = CharacterBoundsUpdateEvent;
|
||||
pub const tonechange = RTCDTMFToneChangeEvent;
|
||||
@ -2339,7 +2342,7 @@ pub const events = struct {
|
||||
pub const show = Event;
|
||||
pub const validationstatuschange = Event;
|
||||
pub const prerenderingchange = Event;
|
||||
pub const focus = Event;
|
||||
pub const focus = FocusEvent;
|
||||
pub const gatheringstatechange = Event;
|
||||
pub const midimessage = MIDIMessageEvent;
|
||||
pub const selectstart = Event;
|
||||
@ -2457,7 +2460,6 @@ pub const events = struct {
|
||||
pub const play = Event;
|
||||
pub const startstreaming = Event;
|
||||
pub const stop = Event;
|
||||
pub const onerror = Event;
|
||||
pub const navbeforefocus = NavigationEvent;
|
||||
pub const devicemotion = DeviceMotionEvent;
|
||||
pub const selectedcandidatepairchange = Event;
|
||||
@ -2468,18 +2470,19 @@ pub const events = struct {
|
||||
pub const reflectionchange = Event;
|
||||
pub const messageerror = Event;
|
||||
pub const compositionend = CompositionEvent;
|
||||
pub const contextoverflow = Event;
|
||||
pub const removesourcebuffer = Event;
|
||||
pub const repeat = TimeEvent;
|
||||
pub const animationcancel = AnimationEvent;
|
||||
pub const resourcetimingbufferfull = Event;
|
||||
pub const repeat = TimeEvent;
|
||||
pub const connectionavailable = PresentationConnectionAvailableEvent;
|
||||
pub const click = Event;
|
||||
pub const click = PointerEvent;
|
||||
pub const loadingdone = FontFaceSetLoadEvent;
|
||||
pub const soundstart = Event;
|
||||
pub const resourcetimingbufferfull = Event;
|
||||
pub const navigatesuccess = Event;
|
||||
pub const soundstart = Event;
|
||||
pub const webglcontextcreationerror = WebGLContextEvent;
|
||||
pub const zoomlevelchange = Event;
|
||||
pub const dispose = Event;
|
||||
pub const zoomlevelchange = Event;
|
||||
pub const beforeunload = BeforeUnloadEvent;
|
||||
pub const lostpointercapture = PointerEvent;
|
||||
};
|
||||
@ -2504,6 +2507,7 @@ pub const Kind = enum {
|
||||
connect,
|
||||
readingerror,
|
||||
selectionchange,
|
||||
toolchange,
|
||||
open,
|
||||
sinkchange,
|
||||
beforetoggle,
|
||||
@ -2562,6 +2566,7 @@ pub const Kind = enum {
|
||||
freeze,
|
||||
notificationclose,
|
||||
gamepadconnected,
|
||||
quotaoverflow,
|
||||
pointermove,
|
||||
mouseover,
|
||||
cuechange,
|
||||
@ -2770,7 +2775,6 @@ pub const Kind = enum {
|
||||
play,
|
||||
startstreaming,
|
||||
stop,
|
||||
onerror,
|
||||
navbeforefocus,
|
||||
devicemotion,
|
||||
selectedcandidatepairchange,
|
||||
@ -2781,18 +2785,19 @@ pub const Kind = enum {
|
||||
reflectionchange,
|
||||
messageerror,
|
||||
compositionend,
|
||||
contextoverflow,
|
||||
removesourcebuffer,
|
||||
repeat,
|
||||
animationcancel,
|
||||
resourcetimingbufferfull,
|
||||
repeat,
|
||||
connectionavailable,
|
||||
click,
|
||||
loadingdone,
|
||||
soundstart,
|
||||
resourcetimingbufferfull,
|
||||
navigatesuccess,
|
||||
soundstart,
|
||||
webglcontextcreationerror,
|
||||
zoomlevelchange,
|
||||
dispose,
|
||||
zoomlevelchange,
|
||||
beforeunload,
|
||||
lostpointercapture,
|
||||
};
|
||||
@ -2818,6 +2823,7 @@ pub fn Data(comptime kind: Kind) type {
|
||||
.connect => Event,
|
||||
.readingerror => Event,
|
||||
.selectionchange => Event,
|
||||
.toolchange => Event,
|
||||
.open => Event,
|
||||
.sinkchange => Event,
|
||||
.beforetoggle => ToggleEvent,
|
||||
@ -2876,6 +2882,7 @@ pub fn Data(comptime kind: Kind) type {
|
||||
.freeze => Event,
|
||||
.notificationclose => NotificationEvent,
|
||||
.gamepadconnected => GamepadEvent,
|
||||
.quotaoverflow => Event,
|
||||
.pointermove => PointerEvent,
|
||||
.mouseover => MouseEvent,
|
||||
.cuechange => Event,
|
||||
@ -2915,7 +2922,7 @@ pub fn Data(comptime kind: Kind) type {
|
||||
.remove => AnimationPlaybackEvent,
|
||||
.fetch => FetchEvent,
|
||||
.screenschange => Event,
|
||||
.blur => Event,
|
||||
.blur => FocusEvent,
|
||||
.pointerenter => PointerEvent,
|
||||
.characterboundsupdate => CharacterBoundsUpdateEvent,
|
||||
.tonechange => RTCDTMFToneChangeEvent,
|
||||
@ -2966,7 +2973,7 @@ pub fn Data(comptime kind: Kind) type {
|
||||
.show => Event,
|
||||
.validationstatuschange => Event,
|
||||
.prerenderingchange => Event,
|
||||
.focus => Event,
|
||||
.focus => FocusEvent,
|
||||
.gatheringstatechange => Event,
|
||||
.midimessage => MIDIMessageEvent,
|
||||
.selectstart => Event,
|
||||
@ -3084,7 +3091,6 @@ pub fn Data(comptime kind: Kind) type {
|
||||
.play => Event,
|
||||
.startstreaming => Event,
|
||||
.stop => Event,
|
||||
.onerror => Event,
|
||||
.navbeforefocus => NavigationEvent,
|
||||
.devicemotion => DeviceMotionEvent,
|
||||
.selectedcandidatepairchange => Event,
|
||||
@ -3095,23 +3101,342 @@ pub fn Data(comptime kind: Kind) type {
|
||||
.reflectionchange => Event,
|
||||
.messageerror => Event,
|
||||
.compositionend => CompositionEvent,
|
||||
.contextoverflow => Event,
|
||||
.removesourcebuffer => Event,
|
||||
.repeat => TimeEvent,
|
||||
.animationcancel => AnimationEvent,
|
||||
.resourcetimingbufferfull => Event,
|
||||
.repeat => TimeEvent,
|
||||
.connectionavailable => PresentationConnectionAvailableEvent,
|
||||
.click => Event,
|
||||
.click => PointerEvent,
|
||||
.loadingdone => FontFaceSetLoadEvent,
|
||||
.soundstart => Event,
|
||||
.resourcetimingbufferfull => Event,
|
||||
.navigatesuccess => Event,
|
||||
.soundstart => Event,
|
||||
.webglcontextcreationerror => WebGLContextEvent,
|
||||
.zoomlevelchange => Event,
|
||||
.dispose => Event,
|
||||
.zoomlevelchange => Event,
|
||||
.beforeunload => BeforeUnloadEvent,
|
||||
.lostpointercapture => PointerEvent,
|
||||
};
|
||||
}
|
||||
|
||||
/// Resolves an event `type` string to its `Kind`, or null if unknown.
|
||||
pub fn kindForType(type_str: []const u8) ?Kind {
|
||||
return kind_for_type.get(type_str);
|
||||
}
|
||||
const kind_for_type = std.StaticStringMap(Kind).initComptime(.{
|
||||
.{ "DOMActivate", .DOMActivate },
|
||||
.{ "gotpointercapture", .gotpointercapture },
|
||||
.{ "keyframerequest", .keyframerequest },
|
||||
.{ "signalingstatechange", .signalingstatechange },
|
||||
.{ "promptaction", .promptaction },
|
||||
.{ "afterprint", .afterprint },
|
||||
.{ "popstate", .popstate },
|
||||
.{ "scroll", .scroll },
|
||||
.{ "promptdismiss", .promptdismiss },
|
||||
.{ "fullscreenerror", .fullscreenerror },
|
||||
.{ "beforeinput", .beforeinput },
|
||||
.{ "periodicsync", .periodicsync },
|
||||
.{ "durationchange", .durationchange },
|
||||
.{ "dragenter", .dragenter },
|
||||
.{ "pagereveal", .pagereveal },
|
||||
.{ "connect", .connect },
|
||||
.{ "readingerror", .readingerror },
|
||||
.{ "selectionchange", .selectionchange },
|
||||
.{ "toolchange", .toolchange },
|
||||
.{ "open", .open },
|
||||
.{ "sinkchange", .sinkchange },
|
||||
.{ "beforetoggle", .beforetoggle },
|
||||
.{ "suspend", .@"suspend" },
|
||||
.{ "versionchange", .versionchange },
|
||||
.{ "pointerrawupdate", .pointerrawupdate },
|
||||
.{ "result", .result },
|
||||
.{ "change", .change },
|
||||
.{ "drop", .drop },
|
||||
.{ "playing", .playing },
|
||||
.{ "contextlost", .contextlost },
|
||||
.{ "message", .message },
|
||||
.{ "voiceschanged", .voiceschanged },
|
||||
.{ "update", .update },
|
||||
.{ "selectend", .selectend },
|
||||
.{ "location", .location },
|
||||
.{ "keypress", .keypress },
|
||||
.{ "connecting", .connecting },
|
||||
.{ "downloadprogress", .downloadprogress },
|
||||
.{ "boundary", .boundary },
|
||||
.{ "leavepictureinpicture", .leavepictureinpicture },
|
||||
.{ "mouseenter", .mouseenter },
|
||||
.{ "error", .@"error" },
|
||||
.{ "hashchange", .hashchange },
|
||||
.{ "keystatuseschange", .keystatuseschange },
|
||||
.{ "focusin", .focusin },
|
||||
.{ "start", .start },
|
||||
.{ "auxclick", .auxclick },
|
||||
.{ "mouseout", .mouseout },
|
||||
.{ "currententrychange", .currententrychange },
|
||||
.{ "resize", .resize },
|
||||
.{ "compositionstart", .compositionstart },
|
||||
.{ "sync", .sync },
|
||||
.{ "textInput", .textInput },
|
||||
.{ "dragend", .dragend },
|
||||
.{ "timeupdate", .timeupdate },
|
||||
.{ "transitioncancel", .transitioncancel },
|
||||
.{ "release", .release },
|
||||
.{ "pointerover", .pointerover },
|
||||
.{ "reading", .reading },
|
||||
.{ "transitionstart", .transitionstart },
|
||||
.{ "load", .load },
|
||||
.{ "securitypolicyviolation", .securitypolicyviolation },
|
||||
.{ "cut", .cut },
|
||||
.{ "canplaythrough", .canplaythrough },
|
||||
.{ "squeeze", .squeeze },
|
||||
.{ "touchcancel", .touchcancel },
|
||||
.{ "pointerdown", .pointerdown },
|
||||
.{ "backgroundfetchfail", .backgroundfetchfail },
|
||||
.{ "controllerchange", .controllerchange },
|
||||
.{ "toggle", .toggle },
|
||||
.{ "isolationchange", .isolationchange },
|
||||
.{ "formdata", .formdata },
|
||||
.{ "sourceended", .sourceended },
|
||||
.{ "ended", .ended },
|
||||
.{ "freeze", .freeze },
|
||||
.{ "notificationclose", .notificationclose },
|
||||
.{ "gamepadconnected", .gamepadconnected },
|
||||
.{ "quotaoverflow", .quotaoverflow },
|
||||
.{ "pointermove", .pointermove },
|
||||
.{ "mouseover", .mouseover },
|
||||
.{ "cuechange", .cuechange },
|
||||
.{ "gattserverdisconnected", .gattserverdisconnected },
|
||||
.{ "pointerup", .pointerup },
|
||||
.{ "chargingchange", .chargingchange },
|
||||
.{ "finish", .finish },
|
||||
.{ "servicechanged", .servicechanged },
|
||||
.{ "devicechange", .devicechange },
|
||||
.{ "mousedown", .mousedown },
|
||||
.{ "shippingaddresschange", .shippingaddresschange },
|
||||
.{ "transitionend", .transitionend },
|
||||
.{ "slotchange", .slotchange },
|
||||
.{ "rejectionhandled", .rejectionhandled },
|
||||
.{ "unhandledrejection", .unhandledrejection },
|
||||
.{ "mouseleave", .mouseleave },
|
||||
.{ "loadstart", .loadstart },
|
||||
.{ "touchend", .touchend },
|
||||
.{ "icecandidateerror", .icecandidateerror },
|
||||
.{ "copy", .copy },
|
||||
.{ "paymentrequest", .paymentrequest },
|
||||
.{ "pointerout", .pointerout },
|
||||
.{ "prioritychange", .prioritychange },
|
||||
.{ "audiostart", .audiostart },
|
||||
.{ "nomatch", .nomatch },
|
||||
.{ "submit", .submit },
|
||||
.{ "processorerror", .processorerror },
|
||||
.{ "addsourcebuffer", .addsourcebuffer },
|
||||
.{ "contextmenu", .contextmenu },
|
||||
.{ "closing", .closing },
|
||||
.{ "updatefound", .updatefound },
|
||||
.{ "waitingforkey", .waitingforkey },
|
||||
.{ "DOMFocusOut", .DOMFocusOut },
|
||||
.{ "capturehandlechange", .capturehandlechange },
|
||||
.{ "volumechange", .volumechange },
|
||||
.{ "disconnect", .disconnect },
|
||||
.{ "remove", .remove },
|
||||
.{ "fetch", .fetch },
|
||||
.{ "screenschange", .screenschange },
|
||||
.{ "blur", .blur },
|
||||
.{ "pointerenter", .pointerenter },
|
||||
.{ "characterboundsupdate", .characterboundsupdate },
|
||||
.{ "tonechange", .tonechange },
|
||||
.{ "seeked", .seeked },
|
||||
.{ "dequeue", .dequeue },
|
||||
.{ "pointerlockchange", .pointerlockchange },
|
||||
.{ "stalled", .stalled },
|
||||
.{ "install", .install },
|
||||
.{ "input", .input },
|
||||
.{ "audioprocess", .audioprocess },
|
||||
.{ "touchmove", .touchmove },
|
||||
.{ "transitionrun", .transitionrun },
|
||||
.{ "progress", .progress },
|
||||
.{ "scrollend", .scrollend },
|
||||
.{ "dragstart", .dragstart },
|
||||
.{ "datachannel", .datachannel },
|
||||
.{ "loadingerror", .loadingerror },
|
||||
.{ "icegatheringstatechange", .icegatheringstatechange },
|
||||
.{ "pointercancel", .pointercancel },
|
||||
.{ "dischargingtimechange", .dischargingtimechange },
|
||||
.{ "frameratechange", .frameratechange },
|
||||
.{ "autofill", .autofill },
|
||||
.{ "resume", .@"resume" },
|
||||
.{ "audioend", .audioend },
|
||||
.{ "levelchange", .levelchange },
|
||||
.{ "chargingtimechange", .chargingtimechange },
|
||||
.{ "waiting", .waiting },
|
||||
.{ "availabilitychanged", .availabilitychanged },
|
||||
.{ "dblclick", .dblclick },
|
||||
.{ "canmakepayment", .canmakepayment },
|
||||
.{ "navigate", .navigate },
|
||||
.{ "unload", .unload },
|
||||
.{ "push", .push },
|
||||
.{ "paste", .paste },
|
||||
.{ "redraw", .redraw },
|
||||
.{ "animationstart", .animationstart },
|
||||
.{ "beforeinstallprompt", .beforeinstallprompt },
|
||||
.{ "beforexrselect", .beforexrselect },
|
||||
.{ "currentscreenchange", .currentscreenchange },
|
||||
.{ "dataavailable", .dataavailable },
|
||||
.{ "ratechange", .ratechange },
|
||||
.{ "orientationchange", .orientationchange },
|
||||
.{ "trackedsourceschange", .trackedsourceschange },
|
||||
.{ "updateend", .updateend },
|
||||
.{ "upgradeneeded", .upgradeneeded },
|
||||
.{ "webglcontextrestored", .webglcontextrestored },
|
||||
.{ "statechange", .statechange },
|
||||
.{ "show", .show },
|
||||
.{ "validationstatuschange", .validationstatuschange },
|
||||
.{ "prerenderingchange", .prerenderingchange },
|
||||
.{ "focus", .focus },
|
||||
.{ "gatheringstatechange", .gatheringstatechange },
|
||||
.{ "midimessage", .midimessage },
|
||||
.{ "selectstart", .selectstart },
|
||||
.{ "layoutchange", .layoutchange },
|
||||
.{ "loading", .loading },
|
||||
.{ "advertisementreceived", .advertisementreceived },
|
||||
.{ "contextrestored", .contextrestored },
|
||||
.{ "fullscreenchange", .fullscreenchange },
|
||||
.{ "inputreport", .inputreport },
|
||||
.{ "unmute", .unmute },
|
||||
.{ "notificationclick", .notificationclick },
|
||||
.{ "addtrack", .addtrack },
|
||||
.{ "bufferedamountlow", .bufferedamountlow },
|
||||
.{ "begin", .begin },
|
||||
.{ "deviceorientationabsolute", .deviceorientationabsolute },
|
||||
.{ "keyup", .keyup },
|
||||
.{ "select", .select },
|
||||
.{ "animationiteration", .animationiteration },
|
||||
.{ "soundend", .soundend },
|
||||
.{ "textupdate", .textupdate },
|
||||
.{ "dragleave", .dragleave },
|
||||
.{ "DOMFocusIn", .DOMFocusIn },
|
||||
.{ "mouseup", .mouseup },
|
||||
.{ "canplay", .canplay },
|
||||
.{ "online", .online },
|
||||
.{ "backgroundfetchabort", .backgroundfetchabort },
|
||||
.{ "drag", .drag },
|
||||
.{ "navnotarget", .navnotarget },
|
||||
.{ "speechstart", .speechstart },
|
||||
.{ "pageshow", .pageshow },
|
||||
.{ "blocked", .blocked },
|
||||
.{ "captureaction", .captureaction },
|
||||
.{ "pause", .pause },
|
||||
.{ "payerdetailchange", .payerdetailchange },
|
||||
.{ "activate", .activate },
|
||||
.{ "dragover", .dragover },
|
||||
.{ "sourceopen", .sourceopen },
|
||||
.{ "updatestart", .updatestart },
|
||||
.{ "paymentmethodchange", .paymentmethodchange },
|
||||
.{ "serviceadded", .serviceadded },
|
||||
.{ "emptied", .emptied },
|
||||
.{ "visibilitychange", .visibilitychange },
|
||||
.{ "wheel", .wheel },
|
||||
.{ "timeout", .timeout },
|
||||
.{ "cookiechange", .cookiechange },
|
||||
.{ "keydown", .keydown },
|
||||
.{ "animationend", .animationend },
|
||||
.{ "uncapturederror", .uncapturederror },
|
||||
.{ "command", .command },
|
||||
.{ "pageswap", .pageswap },
|
||||
.{ "beforeprint", .beforeprint },
|
||||
.{ "mark", .mark },
|
||||
.{ "rtctransform", .rtctransform },
|
||||
.{ "endstreaming", .endstreaming },
|
||||
.{ "loadend", .loadend },
|
||||
.{ "backgroundfetchclick", .backgroundfetchclick },
|
||||
.{ "pointerlockerror", .pointerlockerror },
|
||||
.{ "geometrychange", .geometrychange },
|
||||
.{ "fencedtreeclick", .fencedtreeclick },
|
||||
.{ "characteristicvaluechanged", .characteristicvaluechanged },
|
||||
.{ "connectionstatechange", .connectionstatechange },
|
||||
.{ "exit", .exit },
|
||||
.{ "deviceorientation", .deviceorientation },
|
||||
.{ "textformatupdate", .textformatupdate },
|
||||
.{ "contentdelete", .contentdelete },
|
||||
.{ "enterpictureinpicture", .enterpictureinpicture },
|
||||
.{ "track", .track },
|
||||
.{ "mute", .mute },
|
||||
.{ "visibilitymaskchange", .visibilitymaskchange },
|
||||
.{ "compositionupdate", .compositionupdate },
|
||||
.{ "bufferedchange", .bufferedchange },
|
||||
.{ "touchstart", .touchstart },
|
||||
.{ "pointerleave", .pointerleave },
|
||||
.{ "gamepaddisconnected", .gamepaddisconnected },
|
||||
.{ "capturedmousechange", .capturedmousechange },
|
||||
.{ "portalactivate", .portalactivate },
|
||||
.{ "pagehide", .pagehide },
|
||||
.{ "serviceremoved", .serviceremoved },
|
||||
.{ "DOMContentLoaded", .DOMContentLoaded },
|
||||
.{ "negotiationneeded", .negotiationneeded },
|
||||
.{ "webglcontextlost", .webglcontextlost },
|
||||
.{ "removetrack", .removetrack },
|
||||
.{ "clipboardchange", .clipboardchange },
|
||||
.{ "languagechange", .languagechange },
|
||||
.{ "squeezestart", .squeezestart },
|
||||
.{ "storage", .storage },
|
||||
.{ "speechend", .speechend },
|
||||
.{ "icecandidate", .icecandidate },
|
||||
.{ "squeezeend", .squeezeend },
|
||||
.{ "beforematch", .beforematch },
|
||||
.{ "loadeddata", .loadeddata },
|
||||
.{ "shippingoptionchange", .shippingoptionchange },
|
||||
.{ "terminate", .terminate },
|
||||
.{ "abort", .abort },
|
||||
.{ "mousemove", .mousemove },
|
||||
.{ "iceconnectionstatechange", .iceconnectionstatechange },
|
||||
.{ "managedconfigurationchange", .managedconfigurationchange },
|
||||
.{ "offline", .offline },
|
||||
.{ "pushsubscriptionchange", .pushsubscriptionchange },
|
||||
.{ "readystatechange", .readystatechange },
|
||||
.{ "reset", .reset },
|
||||
.{ "seeking", .seeking },
|
||||
.{ "contentvisibilityautostatechange", .contentvisibilityautostatechange },
|
||||
.{ "end", .end },
|
||||
.{ "focusout", .focusout },
|
||||
.{ "loadedmetadata", .loadedmetadata },
|
||||
.{ "enter", .enter },
|
||||
.{ "success", .success },
|
||||
.{ "invalid", .invalid },
|
||||
.{ "inputsourceschange", .inputsourceschange },
|
||||
.{ "appinstalled", .appinstalled },
|
||||
.{ "cancel", .cancel },
|
||||
.{ "close", .close },
|
||||
.{ "encrypted", .encrypted },
|
||||
.{ "play", .play },
|
||||
.{ "startstreaming", .startstreaming },
|
||||
.{ "stop", .stop },
|
||||
.{ "navbeforefocus", .navbeforefocus },
|
||||
.{ "devicemotion", .devicemotion },
|
||||
.{ "selectedcandidatepairchange", .selectedcandidatepairchange },
|
||||
.{ "sourceclose", .sourceclose },
|
||||
.{ "navigateerror", .navigateerror },
|
||||
.{ "backgroundfetchsuccess", .backgroundfetchsuccess },
|
||||
.{ "complete", .complete },
|
||||
.{ "reflectionchange", .reflectionchange },
|
||||
.{ "messageerror", .messageerror },
|
||||
.{ "compositionend", .compositionend },
|
||||
.{ "contextoverflow", .contextoverflow },
|
||||
.{ "removesourcebuffer", .removesourcebuffer },
|
||||
.{ "animationcancel", .animationcancel },
|
||||
.{ "repeat", .repeat },
|
||||
.{ "connectionavailable", .connectionavailable },
|
||||
.{ "click", .click },
|
||||
.{ "loadingdone", .loadingdone },
|
||||
.{ "resourcetimingbufferfull", .resourcetimingbufferfull },
|
||||
.{ "navigatesuccess", .navigatesuccess },
|
||||
.{ "soundstart", .soundstart },
|
||||
.{ "webglcontextcreationerror", .webglcontextcreationerror },
|
||||
.{ "dispose", .dispose },
|
||||
.{ "zoomlevelchange", .zoomlevelchange },
|
||||
.{ "beforeunload", .beforeunload },
|
||||
.{ "lostpointercapture", .lostpointercapture },
|
||||
});
|
||||
|
||||
/// Maps snake_case Zig field names to their camelCase DOM property names.
|
||||
/// Fields whose snake_case equals the DOM name are omitted; callers should
|
||||
/// fall back to the original identifier when no mapping is present.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -37,7 +37,7 @@ pub const Editor = struct {
|
||||
|
||||
pub fn linef(self: *Editor, comptime fmt: []const u8, args: anytype) !void {
|
||||
try self.writeIndent();
|
||||
try self.buffer.writer(self.allocator).print(fmt, args);
|
||||
try self.buffer.print(self.allocator, fmt, args);
|
||||
try self.newline();
|
||||
}
|
||||
|
||||
@ -144,9 +144,9 @@ pub const File = struct {
|
||||
}
|
||||
|
||||
pub fn finish(self: *File) ![]const u8 {
|
||||
var out: std.ArrayList(u8) = .empty;
|
||||
defer out.deinit(self.allocator());
|
||||
var w = out.writer(self.allocator());
|
||||
var out: std.Io.Writer.Allocating = .init(self.allocator());
|
||||
defer out.deinit();
|
||||
const w = &out.writer;
|
||||
|
||||
for (self.decls.items) |decl| {
|
||||
try decl.render(w);
|
||||
@ -154,7 +154,7 @@ pub const File = struct {
|
||||
try w.writeByte('\n');
|
||||
}
|
||||
|
||||
const raw = try out.toOwnedSlice(self.allocator());
|
||||
const raw = out.written();
|
||||
const final_allocator = self.arena.child_allocator;
|
||||
|
||||
const sentinel = try final_allocator.alloc(u8, raw.len + 1);
|
||||
@ -168,9 +168,7 @@ pub const File = struct {
|
||||
};
|
||||
defer tree.deinit(final_allocator);
|
||||
if (tree.errors.len != 0) {
|
||||
const f = try std.Io.Dir.cwd().createFile("ast_error.zig", .{});
|
||||
defer f.close();
|
||||
try f.writeAll(raw);
|
||||
std.debug.print("Invalid generated source:\n{s}\n", .{raw});
|
||||
return error.InvalidGeneratedSource;
|
||||
}
|
||||
|
||||
|
||||
@ -2,7 +2,7 @@ const std = @import("std");
|
||||
const astgen = @import("astgen.zig");
|
||||
const helper = @import("helper.zig");
|
||||
|
||||
const TypeMap = std.StringArrayHashMap([]const u8);
|
||||
const TypeMap = std.array_hash_map.String([]const u8);
|
||||
|
||||
const UnitSupport = struct {
|
||||
length: bool = false,
|
||||
@ -14,22 +14,19 @@ const UnitSupport = struct {
|
||||
};
|
||||
|
||||
const PropMeta = struct {
|
||||
keywords: std.StringArrayHashMap([]const u8),
|
||||
keywords: std.array_hash_map.String([]const u8),
|
||||
units: UnitSupport,
|
||||
href: []const u8,
|
||||
prose: []const u8 = "",
|
||||
};
|
||||
|
||||
pub fn main() !void {
|
||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||
defer _ = gpa.deinit();
|
||||
const allocator = gpa.allocator();
|
||||
try writeFile(allocator, "src/style/generated.zig");
|
||||
pub fn main(init: std.process.Init) !void {
|
||||
try writeFile(init.io, init.gpa, "src/style/generated.zig");
|
||||
std.debug.print("Successfully generated src/style/generated.zig using AST-Driven engine.\n", .{});
|
||||
}
|
||||
|
||||
pub fn writeFile(allocator: std.mem.Allocator, path: []const u8) !void {
|
||||
const source = generate(allocator) catch |err| {
|
||||
pub fn writeFile(io: std.Io, allocator: std.mem.Allocator, path: []const u8) !void {
|
||||
const source = generate(io, allocator) catch |err| {
|
||||
if (err == error.InvalidGeneratedSource) {
|
||||
// We can't easily get the raw buffer from File here without changes,
|
||||
// but we can try to catch it in generate() or just look at what's happening.
|
||||
@ -38,19 +35,17 @@ pub fn writeFile(allocator: std.mem.Allocator, path: []const u8) !void {
|
||||
};
|
||||
defer allocator.free(source);
|
||||
|
||||
const file = try std.Io.Dir.cwd().createFile(path, .{});
|
||||
defer file.close();
|
||||
try file.writeAll(source);
|
||||
const file = try std.Io.Dir.cwd().createFile(io, path, .{});
|
||||
defer file.close(io);
|
||||
try file.writeStreamingAll(io, source);
|
||||
}
|
||||
|
||||
pub fn generate(allocator: std.mem.Allocator) ![]const u8 {
|
||||
pub fn generate(io: std.Io, allocator: std.mem.Allocator) ![]const u8 {
|
||||
var arena = std.heap.ArenaAllocator.init(allocator);
|
||||
defer arena.deinit();
|
||||
const a = arena.allocator();
|
||||
|
||||
const main_file = try std.Io.Dir.cwd().openFile("vendor/webref/ed/css.json", .{});
|
||||
defer main_file.close();
|
||||
const main_content = try main_file.readToEndAlloc(a, 15 * 1024 * 1024);
|
||||
const main_content = try std.Io.Dir.cwd().readFileAlloc(io, "vendor/webref/ed/css.json", a, .unlimited);
|
||||
const main_parsed = try std.json.parseFromSlice(std.json.Value, a, main_content, .{});
|
||||
defer main_parsed.deinit();
|
||||
|
||||
@ -59,26 +54,24 @@ pub fn generate(allocator: std.mem.Allocator) ![]const u8 {
|
||||
const types_json = root.get("types").?.array;
|
||||
const selectors = root.get("selectors").?.array;
|
||||
|
||||
var type_map = TypeMap.init(a);
|
||||
defer type_map.deinit();
|
||||
var type_map: TypeMap = .empty;
|
||||
defer type_map.deinit(a);
|
||||
for (types_json.items) |t| {
|
||||
const name = t.object.get("name").?.string;
|
||||
const syntax = if (t.object.get("syntax")) |s| s.string else "";
|
||||
try type_map.put(name, syntax);
|
||||
try type_map.put(a, name, syntax);
|
||||
}
|
||||
|
||||
var prose_map = std.StringHashMap([]const u8).init(a);
|
||||
defer prose_map.deinit();
|
||||
var kw_prose_map = std.StringHashMap(std.StringHashMap([]const u8)).init(a);
|
||||
defer kw_prose_map.deinit();
|
||||
var dir = try std.Io.Dir.cwd().openDir("vendor/webref/ed/css/", .{ .iterate = true });
|
||||
defer dir.close();
|
||||
var dir = try std.Io.Dir.cwd().openDir(io, "vendor/webref/ed/css/", .{ .iterate = true });
|
||||
defer dir.close(io);
|
||||
var dir_it = dir.iterate();
|
||||
while (try dir_it.next()) |entry| {
|
||||
while (try dir_it.next(io)) |entry| {
|
||||
if (!std.mem.endsWith(u8, entry.name, ".json")) continue;
|
||||
const f = try dir.openFile(entry.name, .{});
|
||||
defer f.close();
|
||||
const fc = try f.readToEndAlloc(a, 5 * 1024 * 1024);
|
||||
const fc = try dir.readFileAlloc(io, entry.name, a, .unlimited);
|
||||
const p = std.json.parseFromSlice(std.json.Value, a, fc, .{}) catch continue;
|
||||
defer p.deinit();
|
||||
if (p.value.object.get("properties")) |props| {
|
||||
@ -101,21 +94,21 @@ pub fn generate(allocator: std.mem.Allocator) ![]const u8 {
|
||||
}
|
||||
}
|
||||
|
||||
var prop_data = std.StringArrayHashMap(PropMeta).init(a);
|
||||
defer prop_data.deinit();
|
||||
var prop_data: std.array_hash_map.String(PropMeta) = .empty;
|
||||
defer prop_data.deinit(a);
|
||||
for (properties.items) |prop| {
|
||||
const name = prop.object.get("name").?.string;
|
||||
const syntax = if (prop.object.get("syntax")) |s| s.string else "";
|
||||
const href = if (prop.object.get("href")) |h| h.string else "";
|
||||
var keywords = std.StringArrayHashMap([]const u8).init(a);
|
||||
try keywords.put("none", "");
|
||||
var keywords: std.array_hash_map.String([]const u8) = .empty;
|
||||
try keywords.put(a, "none", "");
|
||||
const globals = [_][]const u8{ "inherit", "initial", "revert", "revert-layer", "unset" };
|
||||
for (globals) |g| {
|
||||
if (!keywords.contains(g)) try keywords.put(g, "");
|
||||
if (!keywords.contains(g)) try keywords.put(a, g, "");
|
||||
}
|
||||
var units = UnitSupport{};
|
||||
try resolveMetaEnriched(a, syntax, &type_map, &keywords, &units, 0, kw_prose_map.get(name));
|
||||
try prop_data.put(name, .{ .keywords = keywords, .units = units, .href = href, .prose = prose_map.get(name) orelse "" });
|
||||
try prop_data.put(a, name, .{ .keywords = keywords, .units = units, .href = href, .prose = prose_map.get(name) orelse "" });
|
||||
}
|
||||
|
||||
var file = astgen.File.init(allocator);
|
||||
@ -141,14 +134,14 @@ pub fn generate(allocator: std.mem.Allocator) ![]const u8 {
|
||||
const prop_union = try file.addUnion(final_type_name, "enum");
|
||||
try prop_union.setDoc(fa, prop_doc);
|
||||
|
||||
var tags = std.StringArrayHashMap(void).init(a);
|
||||
var tags: std.array_hash_map.String(void) = .empty;
|
||||
var k_it = data.keywords.iterator();
|
||||
while (k_it.next()) |k_entry| {
|
||||
const clean_kw = try cleanName(a, k_entry.key_ptr.*, .snake);
|
||||
if (clean_kw.len > 0 and !tags.contains(clean_kw)) {
|
||||
const kw_doc = if (k_entry.value_ptr.*.len > 0) try docText(fa, null, k_entry.value_ptr.*, null) else "";
|
||||
try prop_union.addField(fa, kw_doc, clean_kw, "", null);
|
||||
try tags.put(clean_kw, {});
|
||||
try tags.put(a, clean_kw, {});
|
||||
}
|
||||
}
|
||||
|
||||
@ -214,8 +207,8 @@ pub fn generate(allocator: std.mem.Allocator) ![]const u8 {
|
||||
try style_struct.addField(fa, "", clean_p, final_type_name, ".none");
|
||||
}
|
||||
|
||||
var selector_tags = std.StringArrayHashMap(void).init(a);
|
||||
defer selector_tags.deinit();
|
||||
var selector_tags: std.array_hash_map.String(void) = .empty;
|
||||
defer selector_tags.deinit(a);
|
||||
for (selectors.items) |sel| {
|
||||
const name = sel.object.get("name").?.string;
|
||||
if (std.mem.indexOf(u8, name, "(") != null) continue;
|
||||
@ -234,7 +227,7 @@ pub fn generate(allocator: std.mem.Allocator) ![]const u8 {
|
||||
}
|
||||
if (is_duplicate) continue;
|
||||
|
||||
try selector_tags.put(clean_s, {});
|
||||
try selector_tags.put(a, clean_s, {});
|
||||
try style_struct.addField(fa, "", clean_s, "?*const Style", "null");
|
||||
}
|
||||
|
||||
@ -298,7 +291,7 @@ fn cleanName(allocator: std.mem.Allocator, name: []const u8, case: enum { pascal
|
||||
return result;
|
||||
}
|
||||
|
||||
fn resolveMetaEnriched(allocator: std.mem.Allocator, syntax: []const u8, type_map: *TypeMap, keywords: *std.StringArrayHashMap([]const u8), units: *UnitSupport, depth: usize, kprose: ?std.StringHashMap([]const u8)) !void {
|
||||
fn resolveMetaEnriched(allocator: std.mem.Allocator, syntax: []const u8, type_map: *TypeMap, keywords: *std.array_hash_map.String([]const u8), units: *UnitSupport, depth: usize, kprose: ?std.StringHashMap([]const u8)) !void {
|
||||
if (depth > 10) return;
|
||||
|
||||
const is_shorthand = std.mem.indexOf(u8, syntax, "{1,4}") != null or std.mem.indexOf(u8, syntax, "{1,2}") != null;
|
||||
@ -360,15 +353,15 @@ fn resolveMetaEnriched(allocator: std.mem.Allocator, syntax: []const u8, type_ma
|
||||
if (std.mem.eql(u8, cleaned, "unset")) break :blk "The unset CSS keyword resets a property to its inherited value if the property naturally inherits from its parent, and to its initial value otherwise.";
|
||||
break :blk "";
|
||||
};
|
||||
try keywords.put(try allocator.dupe(u8, cleaned), try allocator.dupe(u8, pr));
|
||||
try keywords.put(allocator, try allocator.dupe(u8, cleaned), try allocator.dupe(u8, pr));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn docText(allocator: std.mem.Allocator, title: ?[]const u8, prose: []const u8, href: ?[]const u8) ![]const u8 {
|
||||
var list: std.ArrayList(u8) = .empty;
|
||||
defer list.deinit(allocator);
|
||||
const w = list.writer(allocator);
|
||||
var list: std.Io.Writer.Allocating = .init(allocator);
|
||||
defer list.deinit();
|
||||
const w = &list.writer;
|
||||
var first = true;
|
||||
if (title) |t| {
|
||||
try w.print("{s}", .{t});
|
||||
@ -383,7 +376,7 @@ fn docText(allocator: std.mem.Allocator, title: ?[]const u8, prose: []const u8,
|
||||
if (!first) try w.writeAll("\n\n");
|
||||
try w.print("- **W3C**: {s}", .{h});
|
||||
}
|
||||
return try list.toOwnedSlice(allocator);
|
||||
return try allocator.dupe(u8, list.written());
|
||||
}
|
||||
|
||||
fn isZigKeyword(name: []const u8) bool {
|
||||
|
||||
@ -105,19 +105,20 @@ const Interface = struct {
|
||||
};
|
||||
};
|
||||
|
||||
pub fn main() !void {
|
||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||
defer _ = gpa.deinit();
|
||||
const allocator = gpa.allocator();
|
||||
const source = try generate(allocator);
|
||||
pub fn main(init: std.process.Init) !void {
|
||||
const allocator = init.gpa;
|
||||
const io = init.io;
|
||||
|
||||
const source = try generate(io, allocator);
|
||||
defer allocator.free(source);
|
||||
const file = try std.Io.Dir.cwd().createFile("src/runtime/client/events/generated.zig", .{});
|
||||
defer file.close();
|
||||
try file.writeAll(source);
|
||||
try std.Io.Dir.cwd().writeFile(io, .{
|
||||
.sub_path = "src/runtime/client/events/generated.zig",
|
||||
.data = source,
|
||||
});
|
||||
std.debug.print("Generated src/runtime/client/events/generated.zig\n", .{});
|
||||
}
|
||||
|
||||
pub fn generate(allocator: std.mem.Allocator) ![]const u8 {
|
||||
pub fn generate(io: std.Io, allocator: std.mem.Allocator) ![]const u8 {
|
||||
var arena = std.heap.ArenaAllocator.init(allocator);
|
||||
defer arena.deinit();
|
||||
const a = arena.allocator();
|
||||
@ -131,14 +132,12 @@ pub fn generate(allocator: std.mem.Allocator) ![]const u8 {
|
||||
).init(a);
|
||||
var raw_inheritance = std.StringHashMap([]const u8).init(a);
|
||||
|
||||
var spec_dir = try std.Io.Dir.cwd().openDir("vendor/webref/ed/idlparsed", .{ .iterate = true });
|
||||
defer spec_dir.close();
|
||||
var spec_dir = try std.Io.Dir.cwd().openDir(io, "vendor/webref/ed/idlparsed", .{ .iterate = true });
|
||||
defer spec_dir.close(io);
|
||||
var it = spec_dir.iterate();
|
||||
while (try it.next()) |entry| {
|
||||
while (try it.next(io)) |entry| {
|
||||
if (!std.mem.endsWith(u8, entry.name, ".json")) continue;
|
||||
const f = try spec_dir.openFile(entry.name, .{});
|
||||
defer f.close();
|
||||
const content = try f.readToEndAlloc(a, 8 * 1024 * 1024);
|
||||
const content = try spec_dir.readFileAlloc(io, entry.name, a, .unlimited);
|
||||
const parsed = std.json.parseFromSlice(std.json.Value, a, content, .{}) catch continue;
|
||||
const idlp = parsed.value.object.get("idlparsed") orelse continue;
|
||||
const idl_names = (idlp.object.get("idlNames") orelse continue).object;
|
||||
@ -204,9 +203,7 @@ pub fn generate(allocator: std.mem.Allocator) ![]const u8 {
|
||||
// -------------------------------------------------------------------------
|
||||
// Step 3: load events.json → event-name to interface map
|
||||
// -------------------------------------------------------------------------
|
||||
const events_file = try std.Io.Dir.cwd().openFile("vendor/webref/ed/events.json", .{});
|
||||
defer events_file.close();
|
||||
const events_content = try events_file.readToEndAlloc(a, 4 * 1024 * 1024);
|
||||
const events_content = try std.Io.Dir.cwd().readFileAlloc(io, "vendor/webref/ed/events.json", a, .unlimited);
|
||||
const events_parsed = try std.json.parseFromSlice(std.json.Value, a, events_content, .{});
|
||||
const events_array = events_parsed.value.array;
|
||||
|
||||
@ -220,6 +217,22 @@ pub fn generate(allocator: std.mem.Allocator) ![]const u8 {
|
||||
}
|
||||
}
|
||||
|
||||
const interface_overrides = [_]struct { ev: []const u8, iface: []const u8 }{
|
||||
.{ .ev = "click", .iface = "PointerEvent" },
|
||||
.{ .ev = "auxclick", .iface = "PointerEvent" },
|
||||
.{ .ev = "contextmenu", .iface = "PointerEvent" },
|
||||
.{ .ev = "dblclick", .iface = "MouseEvent" },
|
||||
.{ .ev = "focus", .iface = "FocusEvent" },
|
||||
.{ .ev = "blur", .iface = "FocusEvent" },
|
||||
};
|
||||
for (interface_overrides) |o| {
|
||||
if (interfaces.get(o.iface)) |iface| {
|
||||
if (iface.fields.items.len > 0) {
|
||||
try event_map.put(o.ev, o.iface);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Step 4: emit Zig source via astgen
|
||||
// -------------------------------------------------------------------------
|
||||
@ -293,8 +306,8 @@ pub fn generate(allocator: std.mem.Allocator) ![]const u8 {
|
||||
}
|
||||
|
||||
// Emit event name -> struct alias namespace
|
||||
var aliases: std.ArrayListUnmanaged(u8) = .empty;
|
||||
const w = aliases.writer(a);
|
||||
var aliases: std.Io.Writer.Allocating = .init(a);
|
||||
const w = &aliases.writer;
|
||||
try w.writeAll("/// Maps browser event names to their typed structs.\n");
|
||||
try w.writeAll("/// Example: `event.as(events.click, allocator)` or `event.as(events.MouseEvent, allocator)`.\n");
|
||||
try w.writeAll("pub const events = struct {\n");
|
||||
@ -341,6 +354,21 @@ pub fn generate(allocator: std.mem.Allocator) ![]const u8 {
|
||||
try w.writeAll(" };\n");
|
||||
try w.writeAll("}\n\n");
|
||||
|
||||
try w.writeAll("/// Resolves an event `type` string to its `Kind`, or null if unknown.\n");
|
||||
try w.writeAll("pub fn kindForType(type_str: []const u8) ?Kind {\n");
|
||||
try w.writeAll(" return kind_for_type.get(type_str);\n");
|
||||
try w.writeAll("}\n");
|
||||
try w.writeAll("const kind_for_type = std.StaticStringMap(Kind).initComptime(.{\n");
|
||||
for (pairs.items) |p| {
|
||||
const safe = isValidIdent(p.ev) and !isZigKeyword(p.ev);
|
||||
if (safe) {
|
||||
try w.print(" .{{ \"{s}\", .{s} }},\n", .{ p.ev, p.ev });
|
||||
} else {
|
||||
try w.print(" .{{ \"{s}\", .@\"{s}\" }},\n", .{ p.ev, p.ev });
|
||||
}
|
||||
}
|
||||
try w.writeAll("});\n\n");
|
||||
|
||||
// Emit the snake_case → camelCase map used by `Event.as` at runtime to
|
||||
// translate Zig field names back to the original DOM property names.
|
||||
try w.writeAll("/// Maps snake_case Zig field names to their camelCase DOM property names.\n");
|
||||
@ -367,7 +395,7 @@ pub fn generate(allocator: std.mem.Allocator) ![]const u8 {
|
||||
try w.writeAll(" return js_field_names.get(name) orelse name;\n");
|
||||
try w.writeAll("}");
|
||||
|
||||
try file.addRaw(try aliases.toOwnedSlice(a));
|
||||
try file.addRaw(aliases.written());
|
||||
|
||||
return try file.finish();
|
||||
}
|
||||
|
||||
@ -1,11 +1,7 @@
|
||||
const std = @import("std");
|
||||
|
||||
pub fn calcExprSource(allocator: std.mem.Allocator) ![]const u8 {
|
||||
var out: std.ArrayList(u8) = .empty;
|
||||
defer out.deinit(allocator);
|
||||
const w = out.writer(allocator);
|
||||
|
||||
try w.writeAll(
|
||||
return try allocator.dupe(u8,
|
||||
\\pub const CalcExpr = struct {
|
||||
\\ const Self = @This();
|
||||
\\ pub const Unit = enum {
|
||||
@ -138,11 +134,9 @@ pub fn calcExprSource(allocator: std.mem.Allocator) ![]const u8 {
|
||||
\\ return self.combine(Self.numberLeaf(factor), .div);
|
||||
\\ }
|
||||
\\
|
||||
\\ pub fn format(self: Self, w: *std.io.Writer) std.io.Writer.Error!void {
|
||||
\\ pub fn format(self: Self, w: *std.Io.Writer) std.Io.Writer.Error!void {
|
||||
\\ try w.writeAll(self.text());
|
||||
\\ }
|
||||
\\};
|
||||
);
|
||||
|
||||
return try out.toOwnedSlice(allocator);
|
||||
}
|
||||
|
||||
@ -27,6 +27,6 @@
|
||||
"repo": "git@github.com:w3c/webref.git",
|
||||
"https": "https://github.com/w3c/webref",
|
||||
"branch": "curated",
|
||||
"commit": "fb5c62408f5cc49e5cdafa725af9135208bf433d"
|
||||
"commit": "8e1af5a34d1229a3019a34caa424ef7ecd1faeaa"
|
||||
}
|
||||
}
|
||||
|
||||
5
vendor/jsz/js/src/zigjs.ts
vendored
5
vendor/jsz/js/src/zigjs.ts
vendored
@ -237,6 +237,11 @@ export class ZigJS {
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof val === "bigint") {
|
||||
view.setFloat64(out, Number(val), true);
|
||||
return;
|
||||
}
|
||||
|
||||
// Determine our ID
|
||||
let id = this.idPool.pop();
|
||||
if (id === undefined) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user