@@ -5,23 +5,121 @@ import path from "path";
5
5
6
6
import { createGenerator } from "ts-json-schema-generator" ;
7
7
8
- import type { Config } from "ts-json-schema-generator" ;
8
+ import type { Config , SchemaGenerator } from "ts-json-schema-generator" ;
9
9
10
- const config : Config = {
11
- schemaId : "https://khanacademy.org/schema/perseus.json" ,
12
- tsconfig : path . join ( __dirname , ".." , "tsconfig.json" ) ,
13
- jsDoc : "extended" ,
10
+ const RefNameExtractorRegex = / # \/ d e f i n i t i o n s \/ (?< name > .* ) / ;
14
11
15
- // type: "PerseusItem",
12
+ const getRefDefinitionName = ( ref : string ) => {
13
+ const match = ref . match ( RefNameExtractorRegex ) ;
14
+ if ( match ) {
15
+ return match . groups ?. [ "name" ] ;
16
+ }
16
17
} ;
17
18
18
- const outputPath = path . join ( __dirname , "generated/schema.json" ) ;
19
- fs . mkdirSync ( path . dirname ( outputPath ) , { recursive : true } ) ;
19
+ function derefSchema ( schema : ReturnType < SchemaGenerator [ "createSchema" ] > ) {
20
+ const derefIfRef = ( level , key , obj ) => {
21
+ try {
22
+ if (
23
+ obj != null &&
24
+ typeof obj === "object" &&
25
+ ! Array . isArray ( obj ) &&
26
+ "$ref" in obj
27
+ ) {
28
+ const defName = getRefDefinitionName ( obj [ "$ref" ] ) ;
29
+
30
+ if ( defName != null ) {
31
+ return schema . definitions ?. [ defName ] ;
32
+ }
33
+ }
34
+ } catch ( e ) {
35
+ console . log ( key , obj ) ;
36
+ throw e ;
37
+ }
38
+ } ;
39
+
40
+ const processProps = ( path : string [ ] , props , level : number = 0 ) => {
41
+ if ( props == null ) {
42
+ return ;
43
+ }
44
+
45
+ if ( Array . isArray ( props ) ) {
46
+ for ( let i = 0 ; i < props . length ; i ++ ) {
47
+ const newPath = [ ...path , `[${ i } ]` ] ;
48
+ const refTarget = derefIfRef ( level , i , props [ i ] ) ;
49
+ if ( refTarget ) {
50
+ props [ i ] = refTarget ;
51
+ }
52
+
53
+ processProps ( newPath , props [ i ] , level + 1 ) ;
54
+ }
55
+ } else if ( typeof props === "object" ) {
56
+ for ( const [ key , val ] of Object . entries ( props ) ) {
57
+ const newPath = [ ...path , key ] ;
58
+ const refTarget = derefIfRef ( level , key , val ) ;
59
+ if ( refTarget ) {
60
+ props [ key ] = refTarget ;
61
+ }
62
+
63
+ // Stop recursion of nested Renderers
64
+ if ( key === "widgets" && path . indexOf ( "widgets" ) !== - 1 ) {
65
+ delete props [ key ] ;
66
+ continue ;
67
+ }
68
+ processProps ( newPath , props [ key ] , level + 1 ) ;
69
+ }
70
+ }
71
+ } ;
72
+
73
+ processProps ( [ "root" ] , schema . properties ) ;
74
+ processProps ( [ "root" ] , schema . additionalProperties ) ;
75
+
76
+ return schema ;
77
+ }
20
78
21
- const schema = createGenerator ( config ) . createSchema ( "PerseusItem" ) ; // config.type);
22
- const schemaString = JSON . stringify ( schema , null , 2 ) ;
23
- fs . writeFile ( outputPath , schemaString , ( err ) => {
24
- if ( err ) {
25
- throw err ;
79
+ function generateSchema ( ) {
80
+ const config : Config = {
81
+ schemaId : "https://khanacademy.org/schema/perseus.json" ,
82
+ tsconfig : path . join ( __dirname , ".." , "tsconfig.json" ) ,
83
+ jsDoc : "extended" ,
84
+ expose : "all" ,
85
+ functions : "hide" ,
86
+ encodeRefs : false ,
87
+
88
+ skipTypeCheck : true ,
89
+ } ;
90
+
91
+ const generatedDir = path . join ( __dirname , "generated" ) ;
92
+ const rawSchemaPath = path . join ( generatedDir , "schema-raw.json" ) ;
93
+ const schemaPath = path . join ( generatedDir , "schema.json" ) ;
94
+ fs . mkdirSync ( path . dirname ( generatedDir ) , { recursive : true } ) ;
95
+
96
+ console . log ( "Generating schema..." ) ;
97
+ const schema = createGenerator ( config ) . createSchema ( "PerseusItem" ) ;
98
+ const perseusItem = schema . definitions ?. [ "PerseusItem" ] ;
99
+ if ( perseusItem == null ) {
100
+ throw new Error ( "Could not find PerseusItem type in defs!" ) ;
101
+ }
102
+ // Raise up the "PerseusItem" to the root
103
+ schema . type = "object" ;
104
+ for ( const [ name , val ] of Object . entries ( perseusItem ) ) {
105
+ schema [ name ] = val ;
26
106
}
27
- } ) ;
107
+
108
+ const schemaString = JSON . stringify ( schema , undefined , " " ) ;
109
+ fs . writeFileSync ( rawSchemaPath , schemaString ) ;
110
+
111
+ console . log ( "Dereferencing schema..." ) ;
112
+ const dereferencedSchema = derefSchema ( schema ) ;
113
+ fs . writeFileSync (
114
+ schemaPath ,
115
+ JSON . stringify ( dereferencedSchema , undefined , " " ) ,
116
+ ) ;
117
+ }
118
+
119
+ try {
120
+ generateSchema ( ) ;
121
+ } catch ( e ) {
122
+ console . log ( "Script failed!!!" ) ;
123
+ console . error ( e ) ;
124
+ process . exit ( 1 ) ;
125
+ }
0 commit comments