Export more meta-information from schema files, add some introspection tools in Utils

This commit is contained in:
pietervdvn 2022-02-09 22:34:02 +01:00
parent 244e4c294d
commit 5198f5d310
4 changed files with 300 additions and 29 deletions

View file

@ -350,6 +350,88 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
return target;
}
/**
* Walks the specified path into the object till the end.
*
* If a list is encountered, this is tranparently walked recursively on every object.
*
* The leaf objects are replaced by the function
*/
public static WalkPath(path: string[], object: any, replaceLeaf: ((leaf: any) => any)) {
const head = path[0]
if (path.length === 1) {
// We have reached the leaf
const leaf = object[head];
if (leaf !== undefined) {
if(Array.isArray(leaf)){
object[head] = leaf.map(replaceLeaf)
}else{
object[head] = replaceLeaf(leaf)
}
}
return
}
const sub = object[head]
if (sub === undefined) {
return;
}
if (typeof sub !== "object") {
return;
}
if (Array.isArray(sub)) {
sub.forEach(el => Utils.WalkPath(path.slice(1), el, replaceLeaf))
return;
}
Utils.WalkPath(path.slice(1), sub, replaceLeaf)
}
/**
* Walks the specified path into the object till the end.
* If a list is encountered, this is tranparently walked recursively on every object.
*
* The leaf objects are collected in the list
*/
public static CollectPath(path: string[], object: any, collectedList = []): any[] {
if (object === undefined || object === null) {
return collectedList;
}
const head = path[0]
if (path.length === 1) {
// We have reached the leaf
const leaf = object[head];
if (leaf === undefined || leaf === null) {
return collectedList
}
if (Array.isArray(leaf)) {
collectedList.push(...leaf)
} else {
collectedList.push(leaf)
}
return collectedList
}
const sub = object[head]
if (sub === undefined || sub === null) {
return collectedList;
}
if (Array.isArray(sub)) {
sub.forEach(el => Utils.CollectPath(path.slice(1), el, collectedList))
return collectedList;
}
if (typeof sub !== "object") {
return collectedList;
}
return Utils.CollectPath(path.slice(1), sub, collectedList)
}
/**
* Apply a function on every leaf of the JSON; used to rewrite parts of the JSON
* @param json
* @param f
* @constructor
*/
static WalkJson(json: any, f: (v: number | string | boolean | undefined) => any) {
if (json === undefined) {
return f(undefined)

View file

@ -0,0 +1,176 @@
[
{
"path": [],
"type": "object"
},
{
"path": [
"id"
],
"type": "string"
},
{
"path": [
"group"
],
"type": "string"
},
{
"path": [
"labels"
],
"type": "array"
},
{
"path": [
"question"
],
"typeHint": "rendered"
},
{
"path": [
"condition"
],
"type": [
{
"$ref": "#/definitions/AndOrTagConfigJson"
},
{
"type": "string"
}
]
},
{
"path": [
"freeform"
],
"type": "object"
},
{
"path": [
"freeform",
"key"
],
"type": "string"
},
{
"path": [
"freeform",
"type"
],
"type": "string"
},
{
"path": [
"freeform",
"placeholder"
]
},
{
"path": [
"freeform",
"helperArgs"
],
"type": "array"
},
{
"path": [
"freeform",
"addExtraTags"
],
"type": "array"
},
{
"path": [
"freeform",
"inline"
],
"type": "boolean"
},
{
"path": [
"freeform",
"default"
],
"type": "string"
},
{
"path": [
"multiAnswer"
],
"type": "boolean"
},
{
"path": [
"mappings"
],
"type": "array"
},
{
"path": [
"mappings",
"if"
],
"type": [
{
"$ref": "#/definitions/AndOrTagConfigJson"
},
{
"type": "string"
}
]
},
{
"path": [
"mappings",
"then"
],
"typeHint": "rendered"
},
{
"path": [
"mappings",
"icon"
],
"typeHint": "icon",
"type": "string"
},
{
"path": [
"mappings",
"hideInAnswer"
],
"type": [
{
"$ref": "#/definitions/AndOrTagConfigJson"
},
{
"type": [
"string",
"boolean"
]
}
]
},
{
"path": [
"mappings",
"ifnot"
],
"type": [
{
"$ref": "#/definitions/AndOrTagConfigJson"
},
{
"type": "string"
}
]
},
{
"path": [
"mappings",
"addExtraTags"
],
"type": "array"
}
]

View file

@ -40,16 +40,23 @@ function WalkScheme<T>(
fullScheme = fullScheme ?? scheme
var t = onEach(scheme)
if (t !== undefined) {
results.push({
path: [...path],
t
})
}
function walk(v: JsonSchema, pathPart: string) {
if (v === undefined) {
return
}
if(registerSchemePath){
if (registerSchemePath) {
path.push("" + pathPart)
}
results.push(...WalkScheme(onEach, v, registerSchemePath, fullScheme, path, isHandlingReference))
if(registerSchemePath){
if (registerSchemePath) {
path.pop()
}
}
@ -58,21 +65,18 @@ function WalkScheme<T>(
if (scheme === undefined) {
return
}
if(registerSchemePath){
if (registerSchemePath) {
path.push("" + pathPart)
}
scheme.forEach((v, i) => walk(v, "" + i))
if(registerSchemePath){
if (registerSchemePath) {
path.pop()
}
}
if (t !== undefined) {
results.push({
path: [...path],
t
})
} else {
{
walkEach(scheme.enum, "enum")
walkEach(scheme.anyOf, "anyOf")
if (scheme.items !== undefined) {
@ -84,23 +88,43 @@ function WalkScheme<T>(
}
}
if(registerSchemePath){
path.push("properties")
}
if (registerSchemePath) {
path.push("properties")
}
for (const key in scheme.properties) {
const prop = scheme.properties[key]
path.push(key)
results.push(...WalkScheme(onEach, prop, registerSchemePath, fullScheme, path, isHandlingReference))
path.pop()
}
if(registerSchemePath){
path.pop()
if (registerSchemePath) {
path.pop()
}
}
return results
}
function extractMeta(typename: string, path: string) {
const themeSchema = JSON.parse(readFileSync("./Docs/Schemas/" + typename + ".schema.json", "UTF-8"))
const withTypes = WalkScheme((schemePart) => {
if (schemePart.description === undefined) {
return;
}
const typeHint = schemePart.description.split("\n")
.find(line => line.trim().toLocaleLowerCase().startsWith("type:"))
?.substr("type:".length)?.trim()
const type = schemePart.type ?? schemePart.anyOf;
return {typeHint, type}
}, themeSchema)
writeFileSync("./assets/" + path + ".json", JSON.stringify(withTypes.map(({
path,
t
}) => ({path, ...t})), null, " "))
}
function main() {
const allSchemas = ScriptUtils.readDirRecSync("./Docs/Schemas").filter(pth => pth.endsWith("JSC.ts"))
@ -122,19 +146,8 @@ function main() {
writeFileSync(dir + "/" + name + ".schema.json", JSON.stringify(parsed, null, " "), "UTF8")
}
const themeSchema = JSON.parse(readFileSync("./Docs/Schemas/LayoutConfigJson.schema.json", "UTF-8"))
const withTypes =WalkScheme((schemePart) => {
if (schemePart.description === undefined) {
return;
}
const type = schemePart.description.split("\n").filter(line => line.trim().toLocaleLowerCase().startsWith("type: "))[0]
if (type === undefined) {
return undefined
}
return {typeHint: type.substr("type: ".length), type: schemePart.type ?? schemePart.anyOf}
}, themeSchema)
writeFileSync("./assets/layoutconfigmeta.json",JSON.stringify(withTypes.map(({path, t}) => ({path, ...t})), null, " "))
extractMeta("LayoutConfigJson", "layoutconfigmeta")
extractMeta("TagRenderingConfigJson", "tagrenderingconfigmeta")
}