@@ -18,8 +18,10 @@ import "@hyperjump/json-schema/draft-04";
1818import "@hyperjump/json-schema/openapi-3-0" ;
1919import "@hyperjump/json-schema/openapi-3-1" ;
2020import { compile , getSchema } from "@hyperjump/json-schema/experimental" ;
21+ import * as JsonPointer from "@hyperjump/json-pointer" ;
22+ import { jrefTypeOf , Reference } from "@hyperjump/browser/jref" ;
2123import { astToCoverageMap } from "../coverage-util.js" ;
22- import { fromJson } from "../json-util.js" ;
24+ import { fromJson , getNodeFromPointer } from "../json-util.js" ;
2325
2426/**
2527 * @import {
@@ -31,6 +33,8 @@ import { fromJson } from "../json-util.js";
3133 * } from "vitest"
3234 * @import { CoverageMap, CoverageMapData } from "istanbul-lib-coverage"
3335 * @import { SchemaObject } from "@hyperjump/json-schema"
36+ * @import { JRef } from "@hyperjump/browser/jref"
37+ * @import { JsonNode } from "../jsonast.js"
3438 */
3539
3640/** @type CoverageProviderModule */
@@ -142,16 +146,60 @@ class JsonSchemaCoverageProvider {
142146 const schemaPath = path . resolve ( root , file ) ;
143147 const schema = await getSchema ( schemaPath ) ;
144148 const compiledSchema = await compile ( schema ) ;
145- const fileHash = createHash ( "md5" ) . update ( compiledSchema . schemaUri ) . digest ( "hex" ) ;
146- const coverageFilePath = path . resolve ( this . coverageFilesDirectory , fileHash ) ;
149+
147150 const json = await fs . readFile ( schemaPath , "utf-8" ) ;
148151 const tree = fromJson ( json ) ;
149- const coverageMap = astToCoverageMap ( compiledSchema , path . resolve ( root , file ) , tree ) ;
152+ /** @type Record<string, JsonNode> */
153+ const schemaNodes = { } ;
154+ for ( const schemaUri in schema . document . embedded ?? { } ) {
155+ const pointer = this . #findEmbedded( schema . document . root , schemaUri ) ;
156+ schemaNodes [ schemaUri ] = getNodeFromPointer ( tree , pointer ) ;
157+ }
158+ const coverageMap = astToCoverageMap ( compiledSchema , schemaPath , schemaNodes ) ;
159+
160+ const fileHash = createHash ( "md5" ) . update ( compiledSchema . schemaUri ) . digest ( "hex" ) ;
161+ const coverageFilePath = path . resolve ( this . coverageFilesDirectory , fileHash ) ;
150162 await fs . writeFile ( coverageFilePath , JSON . stringify ( coverageMap ) ) ;
151163 }
152164 }
153165 }
154166
167+ /** @type (node: JRef, uri: string, pointer?: string) => Generator<[string, JRef]> */
168+ * #allSchemaNodes( node , uri , pointer = "" ) {
169+ yield [ pointer , node ] ;
170+
171+ switch ( jrefTypeOf ( node ) ) {
172+ case "object" :
173+ const jrefObject = /** @type Record<string, JRef> */ ( node ) ;
174+ for ( const key in jrefObject ) {
175+ yield * this . #allSchemaNodes( jrefObject [ key ] , uri , JsonPointer . append ( key , pointer ) ) ;
176+ }
177+ break ;
178+
179+ case "array" :
180+ const jrefArray = /** @type JRef[] */ ( node ) ;
181+ let index = 0 ;
182+ for ( const item of jrefArray ) {
183+ yield * this . #allSchemaNodes( item , uri , JsonPointer . append ( `${ index ++ } ` , pointer ) ) ;
184+ }
185+ break ;
186+ }
187+ }
188+
189+ /** @type (root: JRef, uri: string) => string */
190+ #findEmbedded( root , uri ) {
191+ for ( const [ pointer , node ] of this . #allSchemaNodes( root , uri ) ) {
192+ if ( node instanceof Reference ) {
193+ const json = node . toJSON ( ) ;
194+ if ( typeof json === "object" && json !== null && ! ( "$ref" in json ) && node . href === uri ) {
195+ return pointer ;
196+ }
197+ }
198+ }
199+
200+ return "" ;
201+ }
202+
155203 /** @type () => Promise<void> */
156204 async cleanAfterRun ( ) {
157205 await fs . rm ( this . coverageFilesDirectory , { recursive : true } ) ;
0 commit comments