Skip to content

Commit

Permalink
feat: readonly modifier in json schema
Browse files Browse the repository at this point in the history
  • Loading branch information
elyukai committed Jun 7, 2023
1 parent 7cff942 commit 1d2be41
Show file tree
Hide file tree
Showing 3 changed files with 32 additions and 9 deletions.
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,8 @@ Description | all | `markdown` | `description` keyword | Description
`@markdown` | `string` | `boolean` | — | Type: Markdown-formatted text
`@minLength` | `string` | `number` | `minLength` keyword | Minimum Length
`@maxLength` | `string` | `number` | `maxLength` keyword | Maximum Length
`@pattern` | `string` | `string` | `maxLength` keyword | Pattern
`@format` | `string` | `string` | `maxLength` keyword | Format
`@pattern` | `string` | `string` | `pattern` keyword | Pattern
`@format` | `string` | `string` | `format` keyword | Format
`@integer` | `number` | `boolean` | `"type": "integer"` instead of `"type": "number"` | Type: Integer
`@minimum` | `number` | `number` | `minimum` keyword | Minimum
`@maximum` | `number` | `number` | `maximum` keyword | Maximum
Expand All @@ -100,6 +100,7 @@ Description | all | `markdown` | `description` keyword | Description
`@minProperties` | `object` | `number` | `minProperties` keyword | Minimum Properties
`@maxProperties` | `object` | `number` | `maxProperties` keyword | Maximum Properties
`@patternProperties` | `object` | `string` | `patternProperties` keyword | Values matching pattern
`readonly` modifier | property | `boolean` | `readOnly` keyword | Read-only property

#### Boolean tags

Expand Down
8 changes: 7 additions & 1 deletion src/parser/ast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,11 @@ export type RecordNode = {
* The property value.
*/
value: ChildNode

/**
* Is the property read-only?
*/
isReadOnly: boolean
}
}
}
Expand Down Expand Up @@ -493,7 +498,8 @@ const nodeToAst = (node: ts.Node, file: ts.SourceFile, checker: ts.TypeChecker,
{
jsDoc: parseNodeDoc(member),
isRequired: member.questionToken === undefined,
value: resolveTempChildNode(nodeToAst(member.type!, file, checker, program, typeArguments))
value: resolveTempChildNode(nodeToAst(member.type!, file, checker, program, typeArguments)),
isReadOnly: member.modifiers?.some(modifier => modifier.kind === ts.SyntaxKind.ReadonlyKeyword) ?? false
}
]
]
Expand Down
28 changes: 22 additions & 6 deletions src/renderers/jsonSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ interface Annotated {
title?: string
description?: string
default?: unknown
readOnly?: boolean
}

interface ObjectConstraints {
Expand Down Expand Up @@ -198,7 +199,9 @@ const toConstraints = <T extends keyof ConstraintsByType>(jsDoc: Doc | undefined
: []
)

const nodeToDefinition = (spec: JsonSchemaSpec, node: ChildNode): Definition => {
const nodeToDefinition = (spec: JsonSchemaSpec, node: ChildNode, options: { isReadOnly?: boolean } = {}): Definition => {
const { isReadOnly } = options

switch (node.kind) {
case NodeKind.Record: {
return {
Expand All @@ -207,11 +210,12 @@ const nodeToDefinition = (spec: JsonSchemaSpec, node: ChildNode): Definition =>
...toDefault(node.jsDoc),
properties: Object.fromEntries(
Object.entries(node.elements)
.map(([key, config]) => [key, nodeToDefinition(spec, config.value)])),
.map(([key, config]) => [key, nodeToDefinition(spec, config.value, { isReadOnly: config.isReadOnly })])),
required: Object.entries(node.elements)
.filter(([_, config]) => config.isRequired)
.map(([key]) => key),
...toConstraints(node.jsDoc, "object"),
...(isReadOnly ? { readOnly: false } : {}),
additionalProperties: false
}
}
Expand All @@ -225,6 +229,7 @@ const nodeToDefinition = (spec: JsonSchemaSpec, node: ChildNode): Definition =>
[node.pattern]: nodeToDefinition(spec, node.elements)
},
...toConstraints(node.jsDoc, "object"),
...(isReadOnly ? { readOnly: false } : {}),
additionalProperties: false
}
}
Expand All @@ -234,7 +239,8 @@ const nodeToDefinition = (spec: JsonSchemaSpec, node: ChildNode): Definition =>
type: "object",
...toDefault(node.jsDoc),
additionalProperties: nodeToDefinition(spec, node.elements),
...toConstraints(node.jsDoc, "object")
...toConstraints(node.jsDoc, "object"),
...(isReadOnly ? { readOnly: false } : {}),
}
}
}
Expand All @@ -244,14 +250,16 @@ const nodeToDefinition = (spec: JsonSchemaSpec, node: ChildNode): Definition =>
type: "array",
...toDefault(node.jsDoc),
items: nodeToDefinition(spec, node.elements),
...toConstraints(node.jsDoc, "array")
...toConstraints(node.jsDoc, "array"),
...(isReadOnly ? { readOnly: false } : {}),
}
}
case NodeKind.Enumeration: {
return {
...toAnnotations(node.jsDoc),
enum: node.cases.map(({ value }) => value),
...toDefault(node.jsDoc),
...(isReadOnly ? { readOnly: false } : {}),
}
}
case NodeKind.Tuple: {
Expand All @@ -265,6 +273,7 @@ const nodeToDefinition = (spec: JsonSchemaSpec, node: ChildNode): Definition =>
minItems: node.elements.length,
maxItems: node.elements.length,
additionalItems: false,
...(isReadOnly ? { readOnly: false } : {}),
}
case "Draft_2020_12": return {
...toAnnotations(node.jsDoc),
Expand All @@ -274,6 +283,7 @@ const nodeToDefinition = (spec: JsonSchemaSpec, node: ChildNode): Definition =>
minItems: node.elements.length,
maxItems: node.elements.length,
items: false,
...(isReadOnly ? { readOnly: false } : {}),
}
default: throw TypeError("invalid spec")
}
Expand All @@ -283,6 +293,7 @@ const nodeToDefinition = (spec: JsonSchemaSpec, node: ChildNode): Definition =>
...toAnnotations(node.jsDoc),
oneOf: node.cases.map(element => nodeToDefinition(spec, element)),
...toDefault(node.jsDoc),
...(isReadOnly ? { readOnly: false } : {}),
}
}
case NodeKind.Group: {
Expand All @@ -296,6 +307,7 @@ const nodeToDefinition = (spec: JsonSchemaSpec, node: ChildNode): Definition =>
...toAnnotations(node.jsDoc),
const: node.value,
...toDefault(node.jsDoc),
...(isReadOnly ? { readOnly: false } : {}),
}
}
case NodeKind.Reference: {
Expand All @@ -306,6 +318,7 @@ const nodeToDefinition = (spec: JsonSchemaSpec, node: ChildNode): Definition =>
...toAnnotations(node.jsDoc),
$ref: `${externalFilePath}#/${defsKey(spec)}/${qualifiedName}`,
...toDefault(node.jsDoc),
...(isReadOnly ? { readOnly: false } : {}),
}
}
case NodeKind.Token: {
Expand All @@ -315,7 +328,8 @@ const nodeToDefinition = (spec: JsonSchemaSpec, node: ChildNode): Definition =>
...toAnnotations(node.jsDoc),
type: node.jsDoc?.tags.integer ? "integer" : "number",
...toDefault(node.jsDoc),
...toConstraints(node.jsDoc, "number")
...toConstraints(node.jsDoc, "number"),
...(isReadOnly ? { readOnly: false } : {}),
}
}

Expand All @@ -324,7 +338,8 @@ const nodeToDefinition = (spec: JsonSchemaSpec, node: ChildNode): Definition =>
...toAnnotations(node.jsDoc),
type: "string",
...toDefault(node.jsDoc),
...toConstraints(node.jsDoc, "string")
...toConstraints(node.jsDoc, "string"),
...(isReadOnly ? { readOnly: false } : {}),
}
}

Expand All @@ -333,6 +348,7 @@ const nodeToDefinition = (spec: JsonSchemaSpec, node: ChildNode): Definition =>
...toAnnotations(node.jsDoc),
type: "boolean",
...toDefault(node.jsDoc),
...(isReadOnly ? { readOnly: false } : {}),
}
}
}
Expand Down

0 comments on commit 1d2be41

Please sign in to comment.