Compare commits
1 Commits
pierre-sel
...
xapi-typeg
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4b5d97f978 |
1
@xen-orchestra/xapi-typegen/.npmignore
Symbolic link
1
@xen-orchestra/xapi-typegen/.npmignore
Symbolic link
@@ -0,0 +1 @@
|
||||
../../scripts/npmignore
|
||||
90
@xen-orchestra/xapi-typegen/_genTs.mjs
Normal file
90
@xen-orchestra/xapi-typegen/_genTs.mjs
Normal file
@@ -0,0 +1,90 @@
|
||||
let indentLevel = 0
|
||||
|
||||
function indent() {
|
||||
return ' '.repeat(indentLevel)
|
||||
}
|
||||
|
||||
function quoteId(name) {
|
||||
return /^[a-z0-9_]+$/i.test(name) ? name : JSON.stringify(name)
|
||||
}
|
||||
|
||||
function genType(type, schema) {
|
||||
if (type === 'array' && schema.items !== undefined) {
|
||||
const { items } = schema
|
||||
if (Array.isArray(items)) {
|
||||
if (items.length !== 0) {
|
||||
return ['[' + items.map(genTs).join(', ') + ']']
|
||||
}
|
||||
} else {
|
||||
const { type } = items
|
||||
if (type !== undefined && type.length !== 0) {
|
||||
return genTs(items, true) + '[]'
|
||||
} else {
|
||||
return 'unknown[]'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (type !== 'object') {
|
||||
return type
|
||||
}
|
||||
|
||||
const code = []
|
||||
|
||||
const { title } = schema
|
||||
const isInterface = title !== undefined
|
||||
if (isInterface) {
|
||||
code.push('interface ', title, ' ')
|
||||
}
|
||||
const fieldDelimiter = (isInterface ? ';' : ',') + '\n'
|
||||
|
||||
const { additionalProperties, properties } = schema
|
||||
const hasAdditionalProperties = additionalProperties?.type !== undefined
|
||||
const propertiesKeys = Object.keys(properties ?? {})
|
||||
|
||||
if (!hasAdditionalProperties && propertiesKeys.length === 0) {
|
||||
code.push('{}')
|
||||
return code.join('')
|
||||
}
|
||||
|
||||
code.push('{\n')
|
||||
++indentLevel
|
||||
|
||||
for (const name of propertiesKeys.sort()) {
|
||||
const schema = properties[name]
|
||||
code.push(indent(), quoteId(name))
|
||||
if (schema.optional) {
|
||||
code.push('?')
|
||||
}
|
||||
code.push(': ')
|
||||
|
||||
code.push(genTs(schema))
|
||||
|
||||
code.push(fieldDelimiter)
|
||||
}
|
||||
|
||||
if (hasAdditionalProperties) {
|
||||
code.push(indent(), '[key: string]: ', genTs(additionalProperties), fieldDelimiter)
|
||||
}
|
||||
|
||||
--indentLevel
|
||||
code.push(indent(), '}')
|
||||
|
||||
return code.join('')
|
||||
}
|
||||
|
||||
export function genTs(schema, groupMultiple = false) {
|
||||
let { type } = schema
|
||||
if (Array.isArray(type)) {
|
||||
if (type.length !== 1) {
|
||||
const code = type
|
||||
.sort()
|
||||
.map(type => genType(type, schema))
|
||||
.join(' | ')
|
||||
|
||||
return groupMultiple ? '(' + code + ')' : code
|
||||
}
|
||||
type = type[0]
|
||||
}
|
||||
return genType(type, schema)
|
||||
}
|
||||
110
@xen-orchestra/xapi-typegen/_updateSchema.mjs
Normal file
110
@xen-orchestra/xapi-typegen/_updateSchema.mjs
Normal file
@@ -0,0 +1,110 @@
|
||||
const JSON_TYPES = {
|
||||
__proto__: null,
|
||||
|
||||
array: true,
|
||||
boolean: true,
|
||||
null: true,
|
||||
number: true,
|
||||
object: true,
|
||||
string: true,
|
||||
}
|
||||
|
||||
function addType(schema, type) {
|
||||
const previous = schema.type
|
||||
if (previous === undefined) {
|
||||
schema.type = type
|
||||
} else if (Array.isArray(previous)) {
|
||||
if (previous.indexOf(type) === -1) {
|
||||
previous.push(type)
|
||||
}
|
||||
} else if (previous !== type) {
|
||||
schema.type = [previous, type]
|
||||
}
|
||||
}
|
||||
|
||||
function getType(value) {
|
||||
let type = typeof value
|
||||
if (type === 'object') {
|
||||
if (value === null) {
|
||||
type = 'null'
|
||||
} else if (Array.isArray(value)) {
|
||||
type = 'array'
|
||||
}
|
||||
}
|
||||
|
||||
if (type in JSON_TYPES) {
|
||||
return type
|
||||
}
|
||||
throw new TypeError('unsupported type: ' + type)
|
||||
}
|
||||
|
||||
// like Math.max but v1 can be undefined
|
||||
const max = (v1, v2) => (v1 > v2 ? v1 : v2)
|
||||
|
||||
// like Math.min but v1 can be undefined
|
||||
const min = (v1, v2) => (v1 < v2 ? v1 : v2)
|
||||
|
||||
function updateSchema_(path, value, schema = { __proto__: null }, getOption) {
|
||||
if (value === undefined) {
|
||||
schema.optional = true
|
||||
} else {
|
||||
const type = getType(value)
|
||||
addType(schema, type)
|
||||
|
||||
if (type === 'array') {
|
||||
const items = schema.items ?? (schema.items = { __proto__: null })
|
||||
const pathLength = path.length
|
||||
if (Array.isArray(items)) {
|
||||
for (let i = 0, n = value.length; i < n; ++i) {
|
||||
path[pathLength] = i
|
||||
items[i] = updateSchema_(path, value[i], items[i], getOption)
|
||||
}
|
||||
} else {
|
||||
for (let i = 0, n = value.length; i < n; ++i) {
|
||||
path[pathLength] = i
|
||||
updateSchema_(path, value[i], items, getOption)
|
||||
}
|
||||
}
|
||||
path.length = pathLength
|
||||
} else if (type === 'number') {
|
||||
if (getOption('computeMinimum', path)) {
|
||||
schema.minimum = min(schema.minimum, value)
|
||||
}
|
||||
if (getOption('computeMaximum', path)) {
|
||||
schema.maximum = max(schema.maximum, value)
|
||||
}
|
||||
} else if (type === 'object') {
|
||||
const pathLength = path.length
|
||||
const { additionalProperties } = schema
|
||||
if (typeof additionalProperties === 'object') {
|
||||
for (const key of Object.keys(value)) {
|
||||
path[pathLength] = key
|
||||
updateSchema_(path, value[key], additionalProperties, getOption)
|
||||
}
|
||||
} else {
|
||||
const properties = schema.properties ?? (schema.properties = { __proto__: null })
|
||||
|
||||
// handle missing properties
|
||||
for (const key of Object.keys(properties)) {
|
||||
if (!Object.hasOwn(value, key)) {
|
||||
properties[key].optional = true
|
||||
}
|
||||
}
|
||||
|
||||
// handle existing properties
|
||||
for (const key of Object.keys(value)) {
|
||||
path[pathLength] = key
|
||||
properties[key] = updateSchema_(path, value[key], properties[key], getOption)
|
||||
}
|
||||
}
|
||||
path.length = pathLength
|
||||
}
|
||||
}
|
||||
|
||||
return schema
|
||||
}
|
||||
|
||||
export function updateSchema(value, schema, options) {
|
||||
const getOption = options == null ? Function.prototype : typeof options === 'object' ? opt => options[opt] : options
|
||||
return updateSchema_([], value, schema, getOption)
|
||||
}
|
||||
65
@xen-orchestra/xapi-typegen/cli.mjs
Normal file
65
@xen-orchestra/xapi-typegen/cli.mjs
Normal file
@@ -0,0 +1,65 @@
|
||||
import { readFileSync } from 'fs'
|
||||
|
||||
import { genTs } from './_genTs.mjs'
|
||||
import { updateSchema } from './_updateSchema.mjs'
|
||||
|
||||
const upperCamelCase = s =>
|
||||
s
|
||||
.split(/[^a-zA-Z]+/)
|
||||
.map(s => s[0].toUpperCase() + s.slice(1).toLocaleLowerCase())
|
||||
.join('')
|
||||
|
||||
const objects = JSON.parse(readFileSync('./objects.json'))
|
||||
for (const type of Object.keys(objects).sort()) {
|
||||
const schema = {
|
||||
__proto__: null,
|
||||
|
||||
title: upperCamelCase(type),
|
||||
type: 'object',
|
||||
properties: {
|
||||
assigned_ips: {
|
||||
additionalProperties: {},
|
||||
},
|
||||
bios_strings: {
|
||||
additionalProperties: {},
|
||||
},
|
||||
features: {
|
||||
additionalProperties: {},
|
||||
},
|
||||
license_params: {
|
||||
additionalProperties: {},
|
||||
},
|
||||
networks: {
|
||||
additionalProperties: {},
|
||||
},
|
||||
other_config: {
|
||||
additionalProperties: {},
|
||||
},
|
||||
other: {
|
||||
additionalProperties: {},
|
||||
},
|
||||
restrictions: {
|
||||
additionalProperties: {},
|
||||
},
|
||||
sm_config: {
|
||||
additionalProperties: {},
|
||||
},
|
||||
xenstore_data: {
|
||||
additionalProperties: {},
|
||||
},
|
||||
VCPUs_utilisation: {
|
||||
additionalProperties: {},
|
||||
},
|
||||
},
|
||||
}
|
||||
for (const object of Object.values(objects[type])) {
|
||||
updateSchema(object, schema)
|
||||
}
|
||||
for (const name of Object.keys(schema.properties)) {
|
||||
if (schema.properties[name].type === undefined) {
|
||||
delete schema.properties[name]
|
||||
}
|
||||
}
|
||||
|
||||
console.log(genTs(schema))
|
||||
}
|
||||
20
@xen-orchestra/xapi-typegen/package.json
Normal file
20
@xen-orchestra/xapi-typegen/package.json
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"private": true,
|
||||
"name": "@xen-orchestra/xapi-typegen",
|
||||
"homepage": "https://github.com/vatesfr/xen-orchestra/tree/master/@xen-orchestra/xapi-typegen",
|
||||
"bugs": "https://github.com/vatesfr/xen-orchestra/issues",
|
||||
"repository": {
|
||||
"directory": "@xen-orchestra/xapi-typegen",
|
||||
"type": "git",
|
||||
"url": "https://github.com/vatesfr/xen-orchestra.git"
|
||||
},
|
||||
"author": {
|
||||
"name": "Vates SAS",
|
||||
"url": "https://vates.fr"
|
||||
},
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"version": "0.0.0",
|
||||
"engines": {
|
||||
"node": ">=16.9"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user