Skip to content

Commit 94ee9a9

Browse files
committed
Support removals
1 parent 5184d40 commit 94ee9a9

File tree

3 files changed

+55
-26
lines changed

3 files changed

+55
-26
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
removals {
2+
enum AuthenticatorTransport {
3+
smart-card // WebKit only as of 2023-05
4+
}
5+
}

inputfiles/removals/Authenticator.kdl

Lines changed: 0 additions & 3 deletions
This file was deleted.

src/build/patches.ts

Lines changed: 50 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { parse, type Value, type Node } from "kdljs";
1+
import { parse, type Value, type Node, type Document } from "kdljs";
22
import type {
33
Enum,
44
Event,
@@ -19,26 +19,46 @@ type DeepPartial<T> = T extends object
1919
? { [K in keyof T]?: DeepPartial<T[K]> }
2020
: T;
2121

22+
type ParsedType = DeepPartial<WebIdl> & { removals?: DeepPartial<WebIdl> };
23+
2224
interface OverridableMethod extends Omit<Method, "signature"> {
2325
signature: DeepPartial<Signature>[] | Record<number, DeepPartial<Signature>>;
2426
}
2527

26-
function optionalMember<const T>(prop: string, type: T, value?: Value) {
28+
function optionalMember<const T>(
29+
prop: string,
30+
type: T,
31+
value?: Value | DeepPartial<WebIdl>,
32+
) {
2733
if (value === undefined) {
2834
return {};
2935
}
36+
// Support deep property assignment, e.g. prop = "a.b.c"
37+
const propPath = prop.split(".");
3038
if (typeof value !== type) {
31-
throw new Error(`Expected type ${value} for ${prop}`);
39+
throw new Error(
40+
`Expected type ${type} for ${prop} but got ${typeof value}`,
41+
);
3242
}
33-
return {
34-
[prop]: value as T extends "string"
35-
? string
36-
: T extends "number"
37-
? number
38-
: T extends "boolean"
39-
? boolean
40-
: never,
41-
};
43+
// If value is an object, ensure it is not empty (has at least one key)
44+
if (type === "object" && typeof value === "object" && value !== null) {
45+
if (Object.keys(value as object).length === 0) {
46+
return {};
47+
}
48+
}
49+
50+
// Build the nested object dynamically
51+
let nested: any = value as T extends "string"
52+
? string
53+
: T extends "number"
54+
? number
55+
: T extends "boolean"
56+
? boolean
57+
: never;
58+
for (let i = propPath.length - 1; i >= 0; i--) {
59+
nested = { [propPath[i]]: nested };
60+
}
61+
return nested;
4262
}
4363

4464
function string(arg: unknown): string {
@@ -79,8 +99,11 @@ function handleTypeParameters(value: Value) {
7999
/**
80100
* Converts patch files in KDL to match the [types](types.d.ts).
81101
*/
82-
function parseKDL(kdlText: string): DeepPartial<WebIdl> {
83-
const { output, errors } = parse(kdlText);
102+
function parseKDL(kdlText: string | Document): ParsedType {
103+
const { output, errors } =
104+
typeof kdlText === "string"
105+
? parse(kdlText)
106+
: { output: kdlText, errors: [] };
84107

85108
if (errors.length) {
86109
throw new Error("KDL parse errors", { cause: errors });
@@ -91,8 +114,13 @@ function parseKDL(kdlText: string): DeepPartial<WebIdl> {
91114
const mixin: Record<string, DeepPartial<Interface>> = {};
92115
const interfaces: Record<string, DeepPartial<Interface>> = {};
93116
const dictionary: Record<string, DeepPartial<Dictionary>> = {};
117+
let removals: DeepPartial<WebIdl> = {};
94118

95119
for (const node of nodes) {
120+
if (node.name === "removals") {
121+
removals = parseKDL(node.children);
122+
continue;
123+
}
96124
const name = string(node.values[0]);
97125
switch (node.name) {
98126
case "enum":
@@ -107,16 +135,18 @@ function parseKDL(kdlText: string): DeepPartial<WebIdl> {
107135
case "dictionary":
108136
dictionary[name] = handleDictionary(node);
109137
break;
138+
110139
default:
111140
throw new Error(`Unknown node name: ${node.name}`);
112141
}
113142
}
114143

115144
return {
116-
enums: { enum: enums },
117-
mixins: { mixin },
118-
interfaces: { interface: interfaces },
119-
dictionaries: { dictionary },
145+
...optionalMember("enums.enum", "object", enums),
146+
...optionalMember("mixins.mixin", "object", mixin),
147+
...optionalMember("interfaces.interface", "object", interfaces),
148+
...optionalMember("dictionaries.dictionary", "object", dictionary),
149+
...optionalMember("removals", "object", removals),
120150
};
121151
}
122152

@@ -401,16 +431,13 @@ function removeNamesDeep(obj: unknown): unknown {
401431
export default async function readPatches(
402432
folder: "patches" | "removals",
403433
): Promise<any> {
404-
const patchDirectory = new URL(
405-
`../../inputfiles/${folder}/`,
406-
import.meta.url,
407-
);
434+
const patchDirectory = new URL("../../inputfiles/patches/", import.meta.url);
408435
const fileUrls = await getAllFileURLs(patchDirectory);
409436

410437
const parsedContents = await Promise.all(fileUrls.map(readPatch));
411438
const res = parsedContents.reduce((acc, current) => merge(acc, current), {});
412439
if (folder == "removals") {
413-
return removeNamesDeep(res);
440+
return removeNamesDeep(res.removals);
414441
}
415442
return res;
416443
}

0 commit comments

Comments
 (0)