json-schema-ref-parser
json-schema-ref-parser copied to clipboard
Keep $ref some how when bundle
Because $ref offen mean type of something, when render views, need the ref type to select the component.
e.g.
Address.json
{
"type": "object",
"properties": {
"a": {
"type": "string"
}
}
}
User.json
{
"type": "object",
"properties": {
"address": {
"$ref":"Address.json"
}
}
}
If the bundled result is
{
"type": "object",
"properties": {
"address": {
"_ref": "Address.json",
"type": "object",
"properties": {
"a": {
"type": "string"
}
}
}
}
}
Then the frontend can use the "_ref": "Address.json" to match the component more precisely.
There's already some existing discussion going on about this in Issue #135. I think your solution could be acceptable, but I'd like to get some other opinions too
Indeed this looks good to me. Though perhaps persisting $ref instead of _ref would be preferred?
+1
Persisting $ref would confuse tools which know what a $ref is and want to do something about it. It could cause some infinite loops, or just errors from tools which warn you they don't know how to deal with a $ref - which is why you're dereferencing in the first place.
@MikeRalphson has done a bit of this.
I simply allowed the caller to specify a property name for it to be copied to if they wanted $ref preservation.
I like @MikeRalphson's approach. We could add an option, like say dereference.$refRename that defaults to false.
Any falsy value = remove the $ref as it does currently.
Any string value = rename the $ref to the specified value
That would definitely work in our case (from Issue #135) and wouldn't generate a breaking change.
Author of json-schema-to-typescript here. We'd love this features, as it would unblock a handful of issues related to emitting more intelligent TypeScript types.
As specific example, consider the following JSON Schema:
{
additionalProperties: false,
definitions: {
a: {type: 'string'},
b: {type: 'number'}
},
properties: {
c: {
additionalItems: false,
items: [{$ref: '#/definitions/a'}, {$ref: '#/definitions/b'}],
type: 'array'
}
},
title: 'X,
type: 'object'
}
We'd like to emit the following TypeScript types for this schema:
type A = string
type B = number
type X = {
c: [] | [A] | [A, B]
}
Today, we accomplish this by always emitting standalone type names (here, A and B) for tuple members. However, this isn't the right behavior; it would be nice to refine our heuristic, where we look for the presences of $refs as a sign that a type is meant to be reused, and therefore, named (we already use ids and titles for this today).
To do that, we'd love a way to know what a schema's original $ref pointed to. Any key name is fine for us, though you might consider using a Symbol in order to avoid a backwards-incompatible change to this library, and to avoid colliding with existing keys.
As a side note: It's possible to infer something like this today (roughly: traverse over a dereferenced schema and its source schema at the same time, and annotate the dereferenced schema where you see a $ref in its source schema). But this is a hack that doesn't work when a source node is in another file, since $RefParser.resolve(..).get(..) returns a dereferenced schema, not a source schema.
Please let me known if this is not something you're interested in, and I'll go ahead and fork json-schema-ref-parser so we can patch it in for our use case.
@bcherny - Thanks for providing another example of why this feature would be useful. If somebody would like to submit a PR, I'd be happy to review it.
However, I've realized that it's not actually as easy as I suggested previously. The problem with simply keeping the $ref property (or renaming it) is that the same object can be referenced in many different places, so which $ref do you keep?
Instead, it seems like we may need to add an array property (probably with a Symbol to avoid breaking changes) that contains all the refs that pointed to a particular object.
The problem with simply keeping the $ref property (or renaming it) is that the same object can be referenced in many different places, so which $ref do you keep?
@JamesMessinger That's a great catch!
Thinking about it more, I wonder if a simpler and more general-purpose approach might be to avoid setting a property at all, and exposing an onDereference hook instead.
One possible API:
const result = await $RefParser.dereference('schema.json', {
onDereference(originalSourceSchema, dereferencedSchema, originalRefPath, absoluteRefPath) {
// ...
}
})
A consumer might then use this API to build up a mapping:
const map = new Map
const result = await $RefParser.dereference('schema.json', {
onDereference(originalSourceSchema, dereferencedSchema, originalRefPath, absoluteRefPath) {
if (!map.has(dereferencedSchema)) {
map.set(dereferencedSchema, new Set)
}
map.get(dereferencedSchema).add(originalRefPath)
}
})
This approach has a few benefits over the Symbol-based approach:
- It's strictly backwards-compatible, while I can come up with some convoluted cases where the
Symbol-based approach is not - It more closely sticks to the JSON-Schema spec, and avoids having to monkey-patch in properties specific to json-schema-ref-parser (in practice, this would mostly affect consumers that use TypeScript)
- It supports use cases that the
Symbol-based approach does not (say, also collecting the original$ref-containing schemas) - It's extensible without additional breaking changes. eg. down the line we could introduce a 5th parameter for the
onDereferencecallback, without breaking clients (not so for theSymbol-based approach)
I like that idea @bcherny! What does everyone else think? Would this work for your use-case?
/cc @wenerme @maxtuzz @MikeRalphson @philsturgeon @ftheomunhoz
Personally, I've only ever needed to know where a $ref used to be and not what locations previously pointed into a $refed component, and although using a callback would be a little more work (maintaining and comparing to a list of locations as you traversed the dereferenced object, or decorating it yourself), it certainly has the flexibility to enable those use-cases.
This callback approach would work for the "live mock server wants to watch all $ref files for changes so it can reload" use-case. We'd just look file all filepaths and strip of anchors and watch that set of files.
All conversations about changing how $ref should work should be done over here https://github.com/json-schema-org/referencing then when its decided we can all make sure tools follow it.