Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 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 | 2x 2x 10x 10x 10x 18x 18x 10x 17x 17x 17x 17x 18x 18x 16x 21x 16x 6x 6x 6x 6x 2x 3x 24x 24x 2x 24x 24x 24x 30x 17x 30x 17x 17x 30x 30x 28x 28x 17x 24x 24x 11x 2x 1x 10x 10x 10x 2x 2x 2x 2x 2x 2x 2x 1x 1x 8x 5x 1x 1x 1x 16x | import { JSONSchema4 } from "json-schema";
import { CodeMaker } from "codemaker";
import {
camelCase,
pascalCase
} from "change-case";
export interface ResolvedTypes {
type: string;
assignable: boolean;
optional: boolean;
referencable: boolean;
}
export class TypeGenerator {
private readonly emitted = new Set<string>();
public readonly types: {[key: string]: {[key: string]: ResolvedTypes}} = { };
constructor(private readonly type: string) {
}
public addType(typeName: string, def: JSONSchema4) {
Iif (this.emitted.has(typeName)) {
console.log('hier')
return;
}
this.resolveTypes(typeName, def.block || def);
}
public generate(code: CodeMaker) {
for (const type of Object.keys(this.types)) {
const spec = this.types[type];
this.emitStruct(code, type, spec)
code.line();
this.emitted.add(type);
}
}
private resolveTypes(typeName: string, def: JSONSchema4) {
const self = this;
if (def.attributes || def.block_types) {
for (const [ propName, propSpec ] of Object.entries(def.attributes || {})) {
resolveProperty(propName, propSpec as JSONSchema4);
}
for (const [ blockName, blockSpec ] of Object.entries(def.block_types || {})) {
const newTypeName = `${typeName}${pascalCase(blockName)}Props`
self.addType(newTypeName, blockSpec as JSONSchema4)
const emptyType = (Object.keys((blockSpec as JSONSchema4)?.block?.attributes).length === 0)
addProperty(blockName, emptyType ? 'any' : newTypeName, true, true, false)
}
} else {
for (const [ propName, propSpec ] of Object.entries(def || {})) {
resolveProperty(propName, propSpec as JSONSchema4);
}
}
function resolveProperty(name: string, def: JSONSchema4) {
let assignable = true
if ((def.computed && !def.optional) || (self.type !== 'data' && name === 'id')) {
assignable = false
}
const propertyType = self.typeForProperty(def, name, typeName);
const optional = (def.optional || def.computed)
addProperty(name, propertyType, assignable, optional, def.computed)
}
function addProperty(name: string, propertyType: string, assignable: boolean, optional: boolean, referencable: boolean) {
if (!self.types[typeName]) {
self.types[typeName] = {}
}
self.types[typeName][name] = {
type: propertyType,
assignable,
optional,
referencable
}
}
}
private emitStruct(code: CodeMaker, typeName: string, def: {[key: string]: ResolvedTypes}) {
code.openBlock(`export interface ${typeName}`);
for (const propertyName of Object.keys(def)) {
const property = def[propertyName]
if (!property.assignable) continue;
code.line(`readonly ${camelCase(propertyName)}${property.optional ? '?' : ''}: ${property.type};`);
code.line();
}
code.closeBlock();
}
private typeForProperty(def: JSONSchema4, name: string, typeName: string): string {
const comparable = def.type || def
switch (true) {
case (comparable === "string"): return 'string';
case (comparable === "number"): return 'number';
case (comparable === "integer"): return 'number';
case (comparable?.toString() === "bool"): return 'boolean';
case (Array.isArray(comparable)): return this.handleArrayType(comparable as JSONSchema4, name, typeName);
default:
console.log({typeForProperty: def, name, typeName, comparable})
return 'any';
}
}
private handleArrayType(def: JSONSchema4, name: string, typeName: string): string {
const element = ((def.type || def) as Array<any>)
if (Array.isArray(element[element.length - 1])) {
const type = element[0]
const obj = element[element.length - 1]
let newTypeName = undefined;
Iif (this.jsonEqual(obj, ["map", "string"])) {
newTypeName = `{[key: string]: string}`
} else {
newTypeName = `${typeName}${pascalCase(name)}Props`
this.addType(newTypeName, obj[obj.length - 1] as JSONSchema4)
}
switch(type) {
case 'list': return `${newTypeName}[]`;
case 'set': return `Set<${newTypeName}>`;
}
}
switch (true) {
case (this.jsonEqual(element, ["map", "string"])): return '{[k:string]: string}';
case (this.jsonEqual(element, ["map", "bool"])): return '{[k:string]: boolean}';
case (this.jsonEqual(element, ["map", "number"])): return '{[k:string]: number}';
case (this.jsonEqual(element, ["set", "number"])): return 'Set<number>';
case (this.jsonEqual(element, ["set", "string"])): return 'Set<string>';
case (this.jsonEqual(element, ["list", "string"])): return 'string[]';
default:
console.log({handleArrayType: def, name, typeName})
return 'any';
}
}
private jsonEqual(a: any, b: any): boolean {
return JSON.stringify(a) === JSON.stringify(b);
}
} |