1- import { parse , type Value , type Node } from "kdljs" ;
1+ import { parse , type Value , type Node , type Document } from "kdljs" ;
22import 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+
2224interface 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
4464function 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 {
401431export 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