n3ps / json-schema-to-jsdoc

Generate JSDoc from JSON Schema
https://francisn.com/json-schema-to-jsdoc/
ISC License
30 stars 11 forks source link

Supporting nested schemas #41

Open brettz9 opened 4 years ago

brettz9 commented 4 years ago

Not sure how tricky this would end up being, but it would be nice to have support for a hierarchy of types such that something like:

const schemas = {
  $defs: {
    Person: {
      title: 'Person',
      type: 'object',
      properties: {
        name: { type: 'string', description: "A person's name" },
        age: { type: 'integer', description: "A person's age" }
      },
      required: ['name']
    },
    Laborer: {
      title: 'Laborer',
      allOf: [
        {$ref: '#/$defs/Person'},
        {type: 'object', properties: {skillLevel: {type: 'integer'}}},
      ]
    }
  }
}

...could get mapped to:

/**
 * @typedef {PlainObject} Person
 * @property {string} name A person's name
 * @property {integer} [age] A person's age
 */

/**
 * @typedef {Person} Laborer
 * @property {integer} [skillLevel]
 */

(Note how Laborer inherits from Person in the type portion of the Laborer @typedef.)

(I'm using $defs which is the latest JSON Schema draft's equivalent for what had been definitions.)

n3ps commented 4 years ago

I don't know how much of an effort this is. I've seen libraries like json schema random data generators that don't work with $defs and I don't know if the maintainers didn't bother or it was difficult.

brettz9 commented 4 years ago

I can't speak to the tools in question, but this is just JSON after all. Also $defs was simply a name change from definitions, I guess for better avoidance of conflicts, so I don't think it will be too much trouble.

brettz9 commented 4 years ago

I just came across a very interesting part of the JSON Schema spec: http://json-schema.org/draft/2019-09/json-schema-core.html#rfc.appendix.E

The whole section is relevant and worth a read, but to summarize, it suggests creating "an annotation keyword for use in the same schema object alongside of a reference keyword such as "$ref". " It qualifies that this is only for illustrative purposes, but the keyword they offer is "classRelation": "is-a",, and that sounds reasonable to me. This also has the benefit that we could allow multiple inheritance (via unions), e.g.:

const schemas = {
  $defs: {
    Person: {
      title: 'Person',
      type: 'object',
      properties: {
        name: { type: 'string', description: "A person's name" },
        age: { type: 'integer', description: "A person's age" }
      },
      required: ['name']
    },
    Personnel: {
        type: 'object',
        properties: {skillLevel: {type: 'integer'}},
    },
    Laborer: {
      title: 'Laborer',
      allOf: [
        {$ref: '#/$defs/Person', "classRelation": "is-a",},
        {$ref: '#/$defs/Personnel', "classRelation": "is-a",},
      ]
    }
  }
}

...could become:

/**
 * @typedef {PlainObject} Person
 * @property {string} name A person's name
 * @property {integer} [age] A person's age
 */

/**
 * @typedef {PlainObject} Personnel
 * @property {integer} [skillLevel]
 */

/**
 * @typedef {Person&Personnel} Laborer
 */

And if anyOf is used, the operator | could be used in place of & (actually & is not really accepted by jsdoc, but there is an issue for it, and TypeScript (including when just plain JavaScript + jsdoc) supports it).

I think this would be an especially robust approach as well as one somewhat blessed by the JSON Schema spec.