-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathgen-typebox.js
More file actions
151 lines (128 loc) · 4.21 KB
/
gen-typebox.js
File metadata and controls
151 lines (128 loc) · 4.21 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
import { dereferencedDocSchemas as originals } from '@comapeo/core/schema.js'
import { schema2typebox } from 'schema2typebox'
import * as ts from 'typescript'
import { mkdir, writeFile } from 'node:fs/promises'
import { join } from 'node:path'
const TO_GEN = ['observation', 'track', 'preset', 'field']
// We extend the schema instead of assigning values to a clone
// because JSDoc has no clean way to mark nested trees as mutable
// These are not part of the scheme but are added by the DataType class
// to show who authored a particular change
const authorFields = {
createdBy: {
type: 'string',
},
updatedBy: {
type: 'string',
},
}
const observationSchema = extendProperties(originals.observation, {
...authorFields,
attachments: {
type: 'array',
items: {
type: 'object',
properties: {
url: {
type: 'string',
description: 'Path to fetching attachment data',
},
},
},
},
// We add URLs to various `ref` fields inline with how attachments get URLs
presetRef: addUrlField(originals.observation.properties.presetRef),
})
const presetSchema = extendProperties(originals.preset, {
...authorFields,
fieldRefs: addUrlFieldArray(originals.preset.properties.fieldRefs),
iconRef: addUrlField(originals.preset.properties.iconRef),
})
const trackSchema = extendProperties(originals.track, {
...authorFields,
observationRefs: addUrlFieldArray(originals.track.properties.observationRefs),
presetRef: addUrlField(originals.track.properties.presetRef),
})
const fieldSchema = extendProperties(originals.field, authorFields)
const schemas = {
field: fieldSchema,
observation: observationSchema,
preset: presetSchema,
track: trackSchema,
}
const dataTypesDir = join(import.meta.dirname, './src/datatypes')
await mkdir(dataTypesDir, {
recursive: true,
})
// schema2typebox delcars var witu `var` instead of const
// This interferes with our lint rules so we convert it to const
const matchVar = / var /gu
await Promise.all(
TO_GEN.map(async (name) => {
const schema = schemas[name]
console.log(name, 'parsing')
const file = JSON.stringify(schema)
console.log(name, 'generating ts')
const source = await schema2typebox({ input: file })
console.log(name, 'compiling')
// They output TS so we want to translate it directly to JS
const { outputText: compiled } = ts.transpileModule(source, {
compilerOptions: {
module: ts.ModuleKind.ES2022,
},
})
const final = compiled.replace(matchVar, ' const ')
const outPath = join(dataTypesDir, `${name}.js`)
console.log(name, 'saving', outPath)
await writeFile(outPath, final)
console.log(name, 'done!')
}),
)
/**
* Extends the properties of a schema with new properties.
*
* @param {Record<string, any>} schema - The original schema.
* @param {Record<string, any>} properties - New properties to extend the schema with.
* @returns {Record<string, any>} - The extended schema with additional properties.
*/
function extendProperties(schema, properties) {
return {
...schema,
properties: {
...schema.properties,
...properties,
},
}
}
/**
* @typedef {{ properties: Record<string, unknown>, required?: Readonly<string[]> }} SchemaWithProperties
*/
/**
* Adds a URL field to a JSON schema object
* @template {object} T
* @param {T & SchemaWithProperties} schema - The JSON schema object to extend
* @returns {T & { properties: { url: { type: 'string' } } }} The schema with an added url property
*/
function addUrlField(schema) {
const required = schema.required ? schema.required.concat('url') : ['url']
return {
...schema,
properties: {
...schema.properties,
url: { type: 'string' },
},
required,
}
}
/**
* Adds a URL field to the properties of each item in the array within a JSON schema object.
* @template {object} T
* @param {T & { items: SchemaWithProperties }} arraySchema - The JSON schema object with an array as its items to extend
* @returns {T & { items: { properties: { url: { type: 'string' } } } }} The schema with a URL field added to each item's properties
*/
function addUrlFieldArray(arraySchema) {
return {
...arraySchema,
items: addUrlField(arraySchema.items),
}
}