@@ -11,7 +11,7 @@ interface SchemaMap {
11
11
[ id : string ] : Subschema ;
12
12
}
13
13
14
- const EXT_RE = / \. ( y a m l | y m l | j s o n ) $ / i;
14
+ const EXT_RE = / \. ( y a m l | y m l | j s o n ) # ? \/ ? / i;
15
15
export const VIRTUAL_JSON_URL = `file:///_json` ; // fake URL reserved for dynamic JSON
16
16
17
17
function parseYAML ( schema : string ) {
@@ -69,7 +69,7 @@ function parseHttpHeaders(httpHeaders: Record<string, unknown>): Record<string,
69
69
const stringVal = JSON . stringify ( v ) ;
70
70
finalHeaders [ k ] = stringVal ;
71
71
} catch ( err ) {
72
- error ( `Cannot parse key: ${ k } into JSON format. Continuing with the next HTTP header that is specified` ) ;
72
+ error ( `Can’t parse key: ${ k } into JSON format. Continuing with the next HTTP header that is specified` ) ;
73
73
}
74
74
}
75
75
}
@@ -99,9 +99,13 @@ export default async function load(schema: URL | Subschema | Readable, options:
99
99
const hint = options . hint ?? "OpenAPI3" ;
100
100
101
101
// normalize ID
102
- if ( schema . href !== options . rootURL . href ) schemaID = relativePath ( options . rootURL , schema ) ;
102
+ if ( schema . href !== options . rootURL . href ) {
103
+ schemaID = relativePath ( options . rootURL , schema ) ;
104
+ }
103
105
104
- if ( options . urlCache . has ( schemaID ) ) return options . schemas ; // exit early if already indexed
106
+ if ( options . urlCache . has ( schemaID ) ) {
107
+ return options . schemas ; // exit early if already indexed
108
+ }
105
109
options . urlCache . add ( schemaID ) ;
106
110
107
111
const ext = path . extname ( schema . pathname ) . toLowerCase ( ) ;
@@ -138,16 +142,17 @@ export default async function load(schema: URL | Subschema | Readable, options:
138
142
// local file
139
143
else {
140
144
const contents = fs . readFileSync ( schema , "utf8" ) ;
141
- if ( ext === ".yaml" || ext === ".yml" )
145
+ if ( ext === ".yaml" || ext === ".yml" ) {
142
146
options . schemas [ schemaID ] = {
143
147
hint,
144
148
schema : parseYAML ( contents ) as any , // eslint-disable-line @typescript-eslint/no-explicit-any
145
149
} ;
146
- else if ( ext === ".json" )
150
+ } else if ( ext === ".json" ) {
147
151
options . schemas [ schemaID ] = {
148
152
hint,
149
153
schema : parseJSON ( contents ) ,
150
154
} ;
155
+ }
151
156
}
152
157
}
153
158
// 1b. Readable stream
@@ -225,30 +230,23 @@ export default async function load(schema: URL | Subschema | Readable, options:
225
230
hintPath . push ( ...ref . path ) ;
226
231
const hint = isRemoteFullSchema ? "OpenAPI3" : getHint ( { path : hintPath , external : ! ! ref . filename , startFrom : options . hint } ) ;
227
232
228
- // if root schema is remote and this is a relative reference, treat as remote
229
- if ( schema instanceof URL ) {
230
- const nextURL = new URL ( ref . filename , schema ) ;
231
- const nextID = relativePath ( schema , nextURL ) ;
232
- if ( options . urlCache . has ( nextID ) ) return ;
233
- refPromises . push ( load ( nextURL , { ...options , hint } ) ) ;
234
- node . $ref = node . $ref . replace ( ref . filename , nextID ) ;
235
- return ;
236
- }
237
- // otherwise, if $ref is remote use that
238
233
if ( isRemoteURL ( ref . filename ) || isFilepath ( ref . filename ) ) {
239
234
const nextURL = new URL ( ref . filename . startsWith ( "//" ) ? `https://${ ref . filename } ` : ref . filename ) ;
240
- if ( options . urlCache . has ( nextURL . href ) ) return ;
241
235
refPromises . push ( load ( nextURL , { ...options , hint } ) ) ;
242
236
node . $ref = node . $ref . replace ( ref . filename , nextURL . href ) ;
243
237
return ;
244
238
}
245
- // if this is dynamic JSON, we have no idea how to resolve external URLs, so throw here
239
+
240
+ // if this is dynamic JSON (with no cwd), we have no idea how to resolve external URLs, so throw here
246
241
if ( options . rootURL . href === VIRTUAL_JSON_URL ) {
247
- error ( `Can’t resolve "${ ref . filename } " from dynamic JSON. Load this schema from a URL instead .` ) ;
242
+ error ( `Can’t resolve "${ ref . filename } " from dynamic JSON. Either load this schema from a filepath/ URL, or set the \`cwd\` option: \`openapiTS(schema, { cwd: '/path/to/cwd' })\` .` ) ;
248
243
process . exit ( 1 ) ;
249
244
}
250
- error ( `Can’t resolve "${ ref . filename } "` ) ;
251
- process . exit ( 1 ) ;
245
+
246
+ const nextURL = new URL ( ref . filename , schema instanceof URL ? schema : options . rootURL ) ;
247
+ const nextID = relativePath ( schema instanceof URL ? schema : options . rootURL , nextURL ) ;
248
+ refPromises . push ( load ( nextURL , { ...options , hint } ) ) ;
249
+ node . $ref = node . $ref . replace ( ref . filename , nextID ) ;
252
250
} ) ;
253
251
await Promise . all ( refPromises ) ;
254
252
@@ -322,7 +320,12 @@ export interface GetHintOptions {
322
320
startFrom ?: Subschema [ "hint" ] ;
323
321
}
324
322
325
- /** given a path array (an array of indices), what type of object is this? */
323
+ /**
324
+ * Hinting
325
+ * A remote `$ref` may point to anything—A full OpenAPI schema, partial OpenAPI schema, Schema Object, Parameter Object, etc.
326
+ * The only way to parse its contents correctly is to trace the path from the root schema and infer the type it should be.
327
+ * “Hinting” is the process of tracing its lineage back to the root schema to invoke the correct transformations on it.
328
+ */
326
329
export function getHint ( { path, external, startFrom } : GetHintOptions ) : Subschema [ "hint" ] | undefined {
327
330
if ( startFrom && startFrom !== "OpenAPI3" ) {
328
331
switch ( startFrom ) {
@@ -332,6 +335,8 @@ export function getHint({ path, external, startFrom }: GetHintOptions): Subschem
332
335
return getHintFromRequestBodyObject ( path , external ) ;
333
336
case "ResponseObject" :
334
337
return getHintFromResponseObject ( path , external ) ;
338
+ case "SchemaMap" :
339
+ return "SchemaObject" ;
335
340
default :
336
341
return startFrom ;
337
342
}
0 commit comments