@@ -18,8 +18,10 @@ import "@hyperjump/json-schema/draft-04";
18
18
import "@hyperjump/json-schema/openapi-3-0" ;
19
19
import "@hyperjump/json-schema/openapi-3-1" ;
20
20
import { compile , getSchema } from "@hyperjump/json-schema/experimental" ;
21
+ import * as JsonPointer from "@hyperjump/json-pointer" ;
22
+ import { jrefTypeOf , Reference } from "@hyperjump/browser/jref" ;
21
23
import { astToCoverageMap } from "../coverage-util.js" ;
22
- import { fromJson } from "../json-util.js" ;
24
+ import { fromJson , getNodeFromPointer } from "../json-util.js" ;
23
25
24
26
/**
25
27
* @import {
@@ -31,6 +33,8 @@ import { fromJson } from "../json-util.js";
31
33
* } from "vitest"
32
34
* @import { CoverageMap, CoverageMapData } from "istanbul-lib-coverage"
33
35
* @import { SchemaObject } from "@hyperjump/json-schema"
36
+ * @import { JRef } from "@hyperjump/browser/jref"
37
+ * @import { JsonNode } from "../jsonast.js"
34
38
*/
35
39
36
40
/** @type CoverageProviderModule */
@@ -142,16 +146,60 @@ class JsonSchemaCoverageProvider {
142
146
const schemaPath = path . resolve ( root , file ) ;
143
147
const schema = await getSchema ( schemaPath ) ;
144
148
const compiledSchema = await compile ( schema ) ;
145
- const fileHash = createHash ( "md5" ) . update ( compiledSchema . schemaUri ) . digest ( "hex" ) ;
146
- const coverageFilePath = path . resolve ( this . coverageFilesDirectory , fileHash ) ;
149
+
147
150
const json = await fs . readFile ( schemaPath , "utf-8" ) ;
148
151
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 ) ;
150
162
await fs . writeFile ( coverageFilePath , JSON . stringify ( coverageMap ) ) ;
151
163
}
152
164
}
153
165
}
154
166
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
+
155
203
/** @type () => Promise<void> */
156
204
async cleanAfterRun ( ) {
157
205
await fs . rm ( this . coverageFilesDirectory , { recursive : true } ) ;
0 commit comments