@@ -19,8 +19,6 @@ 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-
2422interface OverridableMethod extends Omit < Method , "signature" > {
2523 signature : DeepPartial < Signature > [ ] | Record < number , DeepPartial < Signature > > ;
2624}
@@ -97,30 +95,21 @@ function handleTypeParameters(value: Value) {
9795}
9896
9997/**
100- * Converts patch files in KDL to match the [types](types.d.ts).
98+ * Converts parsed KDL Document nodes to match the [types](types.d.ts).
10199 */
102- function parseKDL ( kdlText : string | Document ) : ParsedType {
103- const { output, errors } =
104- typeof kdlText === "string"
105- ? parse ( kdlText )
106- : { output : kdlText , errors : [ ] } ;
107-
108- if ( errors . length ) {
109- throw new Error ( "KDL parse errors" , { cause : errors } ) ;
110- }
100+ function convertKDLNodes ( nodes : Node [ ] | Document ) : DeepPartial < WebIdl > {
101+ // Accept either Document or array of nodes
102+ const actualNodes : Node [ ] = Array . isArray ( nodes )
103+ ? nodes
104+ : ( nodes as Document ) ;
111105
112- const nodes = output ! ;
113106 const enums : Record < string , Enum > = { } ;
114107 const mixin : Record < string , DeepPartial < Interface > > = { } ;
115108 const interfaces : Record < string , DeepPartial < Interface > > = { } ;
116109 const dictionary : Record < string , DeepPartial < Dictionary > > = { } ;
117- let removals : DeepPartial < WebIdl > = { } ;
118110
119- for ( const node of nodes ) {
120- if ( node . name === "removals" ) {
121- removals = parseKDL ( node . children ) ;
122- continue ;
123- }
111+ for ( const node of actualNodes ) {
112+ // Note: no "removals" handling here; caller is responsible for splitting
124113 const name = string ( node . values [ 0 ] ) ;
125114 switch ( node . name ) {
126115 case "enum" :
@@ -145,7 +134,6 @@ function parseKDL(kdlText: string | Document): ParsedType {
145134 ...optionalMember ( "mixins.mixin" , "object" , mixin ) ,
146135 ...optionalMember ( "interfaces.interface" , "object" , interfaces ) ,
147136 ...optionalMember ( "dictionaries.dictionary" , "object" , dictionary ) ,
148- ...optionalMember ( "removals" , "object" , removals ) ,
149137 } ;
150138}
151139
@@ -397,13 +385,18 @@ async function getAllFileURLs(folder: URL): Promise<URL[]> {
397385}
398386
399387/**
400- * Read and parse a single KDL file.
388+ * Read and parse a single KDL file into its KDL Document structure .
401389 */
402- export async function readPatch ( fileUrl : URL ) : Promise < any > {
390+ async function readPatchDocument ( fileUrl : URL ) : Promise < Document > {
403391 const text = await readFile ( fileUrl , "utf8" ) ;
404- return parseKDL ( text ) ;
392+ const { output, errors } = parse ( text ) ;
393+ if ( errors . length ) {
394+ throw new Error ( `KDL parse errors in ${ fileUrl . toString ( ) } ` , {
395+ cause : errors ,
396+ } ) ;
397+ }
398+ return output ! ;
405399}
406-
407400/**
408401 * Remove all name fields from the object and its children as we don't want
409402 * the names to be part of the removal.
@@ -426,18 +419,53 @@ function removeNamesDeep(obj: unknown): unknown {
426419
427420/**
428421 * Read, parse, and merge all KDL files under the input folder.
422+ * Splits the main patch content and the removals from each file for combined processing.
423+ *
424+ * Returns:
425+ * {
426+ * patches: merged patch contents (excluding removals),
427+ * removalPatches: merged removals, with names stripped
428+ * }
429429 */
430- export default async function readPatches (
431- isRemovals ?: boolean ,
432- ) : Promise < any > {
430+ export default async function readPatches ( ) : Promise < {
431+ patches : any ;
432+ removalPatches : any ;
433+ } > {
433434 const patchDirectory = new URL ( "../../inputfiles/patches/" , import . meta. url ) ;
434435 const fileUrls = await getAllFileURLs ( patchDirectory ) ;
435436
436- const parsedContents = await Promise . all ( fileUrls . map ( readPatch ) ) ;
437- const res = parsedContents . reduce ( ( acc , current ) => merge ( acc , current ) , { } ) ;
438- const { removals, ...withoutRemovals } = res ;
439- if ( isRemovals ) {
440- return removeNamesDeep ( removals ) ;
437+ // Stage 1: Parse all file KDLs into Documents
438+ const documents = await Promise . all ( fileUrls . map ( readPatchDocument ) ) ;
439+
440+ // Stage 2: For each document, split main nodes and removals nodes
441+ const patchNodeGroups : Node [ ] [ ] = [ ] ;
442+ const removalsNodeGroups : Node [ ] [ ] = [ ] ;
443+
444+ for ( const doc of documents ) {
445+ const mainNodes : Node [ ] = [ ] ;
446+ let localRemovalsNodes : Node [ ] = [ ] ;
447+ for ( const node of doc ) {
448+ if ( node . name === "removals" ) {
449+ // Each removals node may itself contain multiple root nodes
450+ localRemovalsNodes = localRemovalsNodes . concat ( node . children ) ;
451+ } else {
452+ mainNodes . push ( node ) ;
453+ }
454+ }
455+ patchNodeGroups . push ( mainNodes ) ;
456+ if ( localRemovalsNodes . length > 0 ) {
457+ removalsNodeGroups . push ( localRemovalsNodes ) ;
458+ }
441459 }
442- return withoutRemovals ;
460+
461+ // Stage 3: Merge all main patches and removals separately using convertKDLNodes
462+ const patchObjs = patchNodeGroups . map ( ( nodes ) => convertKDLNodes ( nodes ) ) ;
463+ const removalObjs = removalsNodeGroups . map ( ( nodes ) => convertKDLNodes ( nodes ) ) ;
464+
465+ const patches = patchObjs . reduce ( ( acc , cur ) => merge ( acc , cur ) , { } ) ;
466+ const removalPatches = removeNamesDeep (
467+ removalObjs . reduce ( ( acc , cur ) => merge ( acc , cur ) , { } ) ,
468+ ) ;
469+
470+ return { patches, removalPatches } ;
443471}
0 commit comments