Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 0 additions & 24 deletions inputfiles/addedTypes.jsonc
Original file line number Diff line number Diff line change
@@ -1,28 +1,4 @@
{
"mixins": {
"mixin": {
"DocumentOrShadowRoot": {
// Manually moved from Document
// See https://github.com/w3c/csswg-drafts/issues/5886 and https://github.com/w3c/csswg-drafts/issues/556
"methods": {
"method": {
"elementFromPoint": {
"name": "elementFromPoint",
"overrideSignatures": [
"elementFromPoint(x: number, y: number): Element | null"
]
},
"elementsFromPoint": {
"name": "elementsFromPoint",
"overrideSignatures": [
"elementsFromPoint(x: number, y: number): Element[]"
]
}
}
}
}
}
},
"interfaces": {
"interface": {
// ImportMeta is not a true DOM interface, but we are forced to declare it as one in order to emit method definitions.
Expand Down
16 changes: 16 additions & 0 deletions inputfiles/patches/cssom-view.kdl
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Manually moved from Document
// See https://github.com/w3c/csswg-drafts/issues/5886 and https://github.com/w3c/csswg-drafts/issues/556
interface-mixin DocumentOrShadowRoot {
method elementFromPoint {
type Element nullable=#true
param x type=long
param y type=long
}
method elementsFromPoint {
type sequence {
type Element
}
param x type=long
param y type=long
}
}
102 changes: 86 additions & 16 deletions src/build/patches.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
import { parse, type Value, type Node } from "kdljs";
import type { Enum, Event, Property, Interface, WebIdl } from "./types.js";
import type {
Enum,
Event,
Property,
Interface,
WebIdl,
Method,
Typed,
} from "./types.js";
import { readdir, readFile } from "fs/promises";
import { merge } from "./helpers.js";

Expand All @@ -25,6 +33,25 @@ function optionalMember<const T>(prop: string, type: T, value?: Value) {
};
}

function string(arg: unknown): string {
if (typeof arg !== "string") {
throw new Error(`Expected a string but found ${typeof arg}`);
}
return arg;
}

function handleTyped(type: Node, returnType?: Value): Typed {
const isTyped = type.name == "type";
const name = string(isTyped ? type.values[0] : returnType);
const subType =
type.children.length > 0 ? handleTyped(type.children[0], name) : undefined;
return {
type: name,
subtype: subType,
...optionalMember("nullable", "boolean", type.properties?.nullable),
};
}

/**
* Converts patch files in KDL to match the [types](types.d.ts).
*/
Expand All @@ -40,10 +67,7 @@ function parseKDL(kdlText: string): DeepPartial<WebIdl> {
const mixin: Record<string, DeepPartial<Interface>> = {};

for (const node of nodes) {
const name = node.values[0];
if (typeof name !== "string") {
throw new Error(`Missing name for ${node.name}`);
}
const name = string(node.values[0]);
switch (node.name) {
case "enum":
enums[name] = handleEnum(node);
Expand All @@ -66,10 +90,7 @@ function parseKDL(kdlText: string): DeepPartial<WebIdl> {
* @param enums The record of enums to update.
*/
function handleEnum(node: Node): Enum {
const name = node.properties?.name || node.values[0];
if (typeof name !== "string") {
throw new Error("Missing enum name");
}
const name = string(node.properties?.name || node.values[0]);
const values: string[] = [];

for (const child of node.children) {
Expand All @@ -88,23 +109,26 @@ function handleEnum(node: Node): Enum {
*/
function handleMixin(node: Node): DeepPartial<Interface> {
const name = node.values[0];
if (typeof name !== "string") {
throw new Error("Missing mixin name");
}

const event: Event[] = [];
const property: Record<string, Partial<Property>> = {};
const method: Record<string, Partial<Method>> = {};

for (const child of node.children) {
switch (child.name) {
case "event":
event.push(handleEvent(child));
break;
case "property": {
const propName = child.values[0] as string;
const propName = string(child.values[0]);
property[propName] = handleProperty(child);
break;
}
case "method": {
const methodName = string(child.values[0]);
method[methodName] = handleMethod(child);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Fine for now but eventually this will need to change to support overloads)

break;
}
default:
throw new Error(`Unknown node name: ${child.name}`);
}
Expand All @@ -114,6 +138,7 @@ function handleMixin(node: Node): DeepPartial<Interface> {
name,
events: { event },
properties: { property },
methods: { method },
...optionalMember("extends", "string", node.properties?.extends),
} as DeepPartial<Interface>;
}
Expand All @@ -124,8 +149,8 @@ function handleMixin(node: Node): DeepPartial<Interface> {
*/
function handleEvent(child: Node): Event {
return {
name: child.values[0] as string,
type: child.properties.type as string,
name: string(child.values[0]),
type: string(child.properties.type),
};
}

Expand All @@ -135,13 +160,58 @@ function handleEvent(child: Node): Event {
*/
function handleProperty(child: Node): Partial<Property> {
return {
name: child.values[0] as string,
name: string(child.values[0]),
...optionalMember("exposed", "string", child.properties?.exposed),
...optionalMember("optional", "boolean", child.properties?.optional),
...optionalMember("overrideType", "string", child.properties?.overrideType),
};
}

/**
* Handles a child node of type "method" and adds it to the method object.
* @param child The child node to handle.
*/
function handleMethod(child: Node): Partial<Method> {
const name = string(child.values[0]);
const returnType = child.properties.returns;

let typeNode: Node | undefined;
const params: { name: string; type: string }[] = [];

for (const c of child.children) {
switch (c.name) {
case "type":
if (typeNode) {
throw new Error(`Method "${name}" has multiple type nodes (invalid)`);
}
typeNode = c;
break;

case "param":
params.push({
name: string(c.values[0]),
type: string(c.properties.type),
});
break;

default:
throw new Error(`Unexpected child "${c.name}" in method "${name}"`);
}
}

if (!typeNode) {
throw new Error(`Method "${name}" is missing a return type`);
}

const signature: Method["signature"] = [
{
param: params,
...handleTyped(typeNode, returnType),
},
];
return { name, signature };
}

/**
* Collect all file URLs in a directory.
*/
Expand Down