Test case
1 generated file · +209 −0test/inputs/json/priority/number-map.json
Aflowreadonly-true--24da4fc107df / TopLevel.js+209 −0
| @@ -0,0 +1,209 @@ | ||
| 1 | +// @flow | |
| 2 | + | |
| 3 | +// To parse this data: | |
| 4 | +// | |
| 5 | +// const Convert = require("./TopLevel"); | |
| 6 | +// | |
| 7 | +// const topLevel = Convert.toTopLevel(json); | |
| 8 | +// | |
| 9 | +// These functions will throw an error if the JSON doesn't | |
| 10 | +// match the expected interface, even if the JSON is valid. | |
| 11 | + | |
| 12 | +export type TopLevel = { | |
| 13 | + +foo: { +[key: string]: number }; | |
| 14 | +}; | |
| 15 | + | |
| 16 | +// Converts JSON strings to/from your types | |
| 17 | +// and asserts the results of JSON.parse at runtime | |
| 18 | +function toTopLevel(json: string): TopLevel { | |
| 19 | + return cast(JSON.parse(json), r("TopLevel")); | |
| 20 | +} | |
| 21 | + | |
| 22 | +function topLevelToJson(value: TopLevel): string { | |
| 23 | + return JSON.stringify(uncast(value, r("TopLevel")), null, 2); | |
| 24 | +} | |
| 25 | + | |
| 26 | +function invalidValue(typ: any, val: any, key: any, parent: any = '') { | |
| 27 | + const prettyTyp = prettyTypeName(typ); | |
| 28 | + const parentText = parent ? ` on ${parent}` : ''; | |
| 29 | + const keyText = key ? ` for key "${key}"` : ''; | |
| 30 | + throw Error(`Invalid value${keyText}${parentText}. Expected ${prettyTyp} but got ${JSON.stringify(val)}`); | |
| 31 | +} | |
| 32 | + | |
| 33 | +function prettyTypeName(typ: any): string { | |
| 34 | + if (Array.isArray(typ)) { | |
| 35 | + if (typ.length === 2 && typ[0] === undefined) { | |
| 36 | + return `an optional ${prettyTypeName(typ[1])}`; | |
| 37 | + } else { | |
| 38 | + return `one of [${typ.map(a => { return prettyTypeName(a); }).join(", ")}]`; | |
| 39 | + } | |
| 40 | + } else if (typeof typ === "object" && typ.literal !== undefined) { | |
| 41 | + return typ.literal; | |
| 42 | + } else { | |
| 43 | + return typeof typ; | |
| 44 | + } | |
| 45 | +} | |
| 46 | + | |
| 47 | +function jsonToJSProps(typ: any): any { | |
| 48 | + if (typ.jsonToJS === undefined) { | |
| 49 | + const map: any = {}; | |
| 50 | + typ.props.forEach((p: any) => map[p.json] = { key: p.js, typ: p.typ }); | |
| 51 | + typ.jsonToJS = map; | |
| 52 | + } | |
| 53 | + return typ.jsonToJS; | |
| 54 | +} | |
| 55 | + | |
| 56 | +function jsToJSONProps(typ: any): any { | |
| 57 | + if (typ.jsToJSON === undefined) { | |
| 58 | + const map: any = {}; | |
| 59 | + typ.props.forEach((p: any) => map[p.js] = { key: p.json, typ: p.typ }); | |
| 60 | + typ.jsToJSON = map; | |
| 61 | + } | |
| 62 | + return typ.jsToJSON; | |
| 63 | +} | |
| 64 | + | |
| 65 | +function transform(val: any, typ: any, getProps: any, key: any = '', parent: any = ''): any { | |
| 66 | + function transformPrimitive(typ: string, val: any): any { | |
| 67 | + if (typeof typ === typeof val) return val; | |
| 68 | + return invalidValue(typ, val, key, parent); | |
| 69 | + } | |
| 70 | + | |
| 71 | + function transformUnion(typs: any[], val: any): any { | |
| 72 | + // val must validate against one typ in typs | |
| 73 | + const l = typs.length; | |
| 74 | + for (let i = 0; i < l; i++) { | |
| 75 | + const typ = typs[i]; | |
| 76 | + try { | |
| 77 | + return transform(val, typ, getProps); | |
| 78 | + } catch (_) {} | |
| 79 | + } | |
| 80 | + return invalidValue(typs, val, key, parent); | |
| 81 | + } | |
| 82 | + | |
| 83 | + function transformEnum(cases: string[], val: any): any { | |
| 84 | + if (cases.indexOf(val) !== -1) return val; | |
| 85 | + return invalidValue(cases.map(a => { return l(a); }), val, key, parent); | |
| 86 | + } | |
| 87 | + | |
| 88 | + function transformArray(typ: any, val: any): any { | |
| 89 | + // val must be an array with no invalid elements | |
| 90 | + if (!Array.isArray(val)) return invalidValue(l("array"), val, key, parent); | |
| 91 | + | |
| 92 | + return val.map(el => transform(el, typ, getProps)); | |
| 93 | + } | |
| 94 | + | |
| 95 | + function transformDate(val: any): any { | |
| 96 | + if (val === null) { | |
| 97 | + return null; | |
| 98 | + } | |
| 99 | + const d = new Date(val); | |
| 100 | + if (isNaN(d.valueOf())) { | |
| 101 | + return invalidValue(l("Date"), val, key, parent); | |
| 102 | + } | |
| 103 | + return d; | |
| 104 | + } | |
| 105 | + | |
| 106 | + function transformObject(props: { [k: string]: any }, additional: any, val: any): any { | |
| 107 | + if (val === null || typeof val !== "object" || Array.isArray(val)) { | |
| 108 | + return invalidValue(l(ref || "object"), val, key, parent); | |
| 109 | + } | |
| 110 | + const result: any = {}; | |
| 111 | + Object.getOwnPropertyNames(props).forEach(key => { | |
| 112 | + const prop = props[key]; | |
| 113 | + const v = Object.prototype.hasOwnProperty.call(val, key) ? val[key] : undefined; | |
| 114 | + result[prop.key] = transform(v, prop.typ, getProps, key, ref); | |
| 115 | + }); | |
| 116 | + Object.getOwnPropertyNames(val).forEach(key => { | |
| 117 | + if (!Object.prototype.hasOwnProperty.call(props, key)) { | |
| 118 | + result[key] = transform(val[key], additional, getProps, key, ref); | |
| 119 | + } | |
| 120 | + }); | |
| 121 | + return result; | |
| 122 | + } | |
| 123 | + | |
| 124 | + if (typ === "any") return val; | |
| 125 | + if (typ === null) { | |
| 126 | + if (val === null) return val; | |
| 127 | + return invalidValue(typ, val, key, parent); | |
| 128 | + } | |
| 129 | + if (typ === false) return invalidValue(typ, val, key, parent); | |
| 130 | + let ref: any = undefined; | |
| 131 | + while (typeof typ === "object" && typ.ref !== undefined) { | |
| 132 | + ref = typ.ref; | |
| 133 | + typ = typeMap[typ.ref]; | |
| 134 | + } | |
| 135 | + if (Array.isArray(typ)) return transformEnum(typ, val); | |
| 136 | + if (typeof typ === "object") { | |
| 137 | + return typ.hasOwnProperty("pattern") ? typeof val === "string" && new RegExp(typ.pattern).test(val) ? val : invalidValue(typ, val, key, parent) | |
| 138 | + : typ.hasOwnProperty("string") ? typeof val === "string" && (typ.min === undefined || Array.from(val).length >= typ.min) && (typ.max === undefined || Array.from(val).length <= typ.max) ? transform(val, typ.string, getProps, key, parent) : invalidValue(typ, val, key, parent) | |
| 139 | + : typ.hasOwnProperty("number") ? typeof val === "number" && (typ.min === undefined || val >= typ.min) && (typ.max === undefined || val <= typ.max) ? transform(val, typ.number, getProps, key, parent) : invalidValue(typ, val, key, parent) | |
| 140 | + : typ.hasOwnProperty("integer") ? typeof val === "number" && val % 1 === 0 ? val : invalidValue(l("integer"), val, key, parent) | |
| 141 | + : typ.hasOwnProperty("unionMembers") ? transformUnion(typ.unionMembers, val) | |
| 142 | + : typ.hasOwnProperty("arrayItems") ? transformArray(typ.arrayItems, val) | |
| 143 | + : typ.hasOwnProperty("props") ? transformObject(getProps(typ), typ.additional, val) | |
| 144 | + : invalidValue(typ, val, key, parent); | |
| 145 | + } | |
| 146 | + // Numbers can be parsed by Date but shouldn't be. | |
| 147 | + if (typ === Date && (typeof val === "string" || val instanceof Date)) return transformDate(val); | |
| 148 | + return transformPrimitive(typ, val); | |
| 149 | +} | |
| 150 | + | |
| 151 | +function cast<T>(val: any, typ: any): T { | |
| 152 | + return transform(val, typ, jsonToJSProps); | |
| 153 | +} | |
| 154 | + | |
| 155 | +function uncast<T>(val: T, typ: any): any { | |
| 156 | + return transform(val, typ, jsToJSONProps); | |
| 157 | +} | |
| 158 | + | |
| 159 | +function l(typ: any) { | |
| 160 | + return { literal: typ }; | |
| 161 | +} | |
| 162 | + | |
| 163 | +function a(typ: any) { | |
| 164 | + return { arrayItems: typ }; | |
| 165 | +} | |
| 166 | + | |
| 167 | +function i(typ: any) { | |
| 168 | + return { integer: typ }; | |
| 169 | +} | |
| 170 | + | |
| 171 | +function p(pattern: any) { | |
| 172 | + return { pattern }; | |
| 173 | +} | |
| 174 | + | |
| 175 | +function s(typ: any, min: any, max: any) { | |
| 176 | + return { string: typ, min, max }; | |
| 177 | +} | |
| 178 | + | |
| 179 | +function n(typ: any, min: any, max: any) { | |
| 180 | + return { number: typ, min, max }; | |
| 181 | +} | |
| 182 | + | |
| 183 | +function u(...typs: any[]) { | |
| 184 | + return { unionMembers: typs }; | |
| 185 | +} | |
| 186 | + | |
| 187 | +function o(props: any[], additional: any) { | |
| 188 | + return { props, additional }; | |
| 189 | +} | |
| 190 | + | |
| 191 | +function m(additional: any) { | |
| 192 | + const props: any[] = []; | |
| 193 | + return { props, additional }; | |
| 194 | +} | |
| 195 | + | |
| 196 | +function r(name: string) { | |
| 197 | + return { ref: name }; | |
| 198 | +} | |
| 199 | + | |
| 200 | +const typeMap: any = { | |
| 201 | + "TopLevel": o([ | |
| 202 | + { json: "foo", js: "foo", typ: m(3.14) }, | |
| 203 | + ], false), | |
| 204 | +}; | |
| 205 | + | |
| 206 | +module.exports = { | |
| 207 | + "topLevelToJson": topLevelToJson, | |
| 208 | + "toTopLevel": toTopLevel, | |
| 209 | +}; |
Test case
1 generated file · +213 −0test/inputs/json/samples/simple-object.json
Aflowreadonly-true--24da4fc107df / TopLevel.js+213 −0
| @@ -0,0 +1,213 @@ | ||
| 1 | +// @flow | |
| 2 | + | |
| 3 | +// To parse this data: | |
| 4 | +// | |
| 5 | +// const Convert = require("./TopLevel"); | |
| 6 | +// | |
| 7 | +// const topLevel = Convert.toTopLevel(json); | |
| 8 | +// | |
| 9 | +// These functions will throw an error if the JSON doesn't | |
| 10 | +// match the expected interface, even if the JSON is valid. | |
| 11 | + | |
| 12 | +export type TopLevel = { | |
| 13 | + +date: number; | |
| 14 | + +title: string; | |
| 15 | + +validity: boolean; | |
| 16 | +}; | |
| 17 | + | |
| 18 | +// Converts JSON strings to/from your types | |
| 19 | +// and asserts the results of JSON.parse at runtime | |
| 20 | +function toTopLevel(json: string): TopLevel { | |
| 21 | + return cast(JSON.parse(json), r("TopLevel")); | |
| 22 | +} | |
| 23 | + | |
| 24 | +function topLevelToJson(value: TopLevel): string { | |
| 25 | + return JSON.stringify(uncast(value, r("TopLevel")), null, 2); | |
| 26 | +} | |
| 27 | + | |
| 28 | +function invalidValue(typ: any, val: any, key: any, parent: any = '') { | |
| 29 | + const prettyTyp = prettyTypeName(typ); | |
| 30 | + const parentText = parent ? ` on ${parent}` : ''; | |
| 31 | + const keyText = key ? ` for key "${key}"` : ''; | |
| 32 | + throw Error(`Invalid value${keyText}${parentText}. Expected ${prettyTyp} but got ${JSON.stringify(val)}`); | |
| 33 | +} | |
| 34 | + | |
| 35 | +function prettyTypeName(typ: any): string { | |
| 36 | + if (Array.isArray(typ)) { | |
| 37 | + if (typ.length === 2 && typ[0] === undefined) { | |
| 38 | + return `an optional ${prettyTypeName(typ[1])}`; | |
| 39 | + } else { | |
| 40 | + return `one of [${typ.map(a => { return prettyTypeName(a); }).join(", ")}]`; | |
| 41 | + } | |
| 42 | + } else if (typeof typ === "object" && typ.literal !== undefined) { | |
| 43 | + return typ.literal; | |
| 44 | + } else { | |
| 45 | + return typeof typ; | |
| 46 | + } | |
| 47 | +} | |
| 48 | + | |
| 49 | +function jsonToJSProps(typ: any): any { | |
| 50 | + if (typ.jsonToJS === undefined) { | |
| 51 | + const map: any = {}; | |
| 52 | + typ.props.forEach((p: any) => map[p.json] = { key: p.js, typ: p.typ }); | |
| 53 | + typ.jsonToJS = map; | |
| 54 | + } | |
| 55 | + return typ.jsonToJS; | |
| 56 | +} | |
| 57 | + | |
| 58 | +function jsToJSONProps(typ: any): any { | |
| 59 | + if (typ.jsToJSON === undefined) { | |
| 60 | + const map: any = {}; | |
| 61 | + typ.props.forEach((p: any) => map[p.js] = { key: p.json, typ: p.typ }); | |
| 62 | + typ.jsToJSON = map; | |
| 63 | + } | |
| 64 | + return typ.jsToJSON; | |
| 65 | +} | |
| 66 | + | |
| 67 | +function transform(val: any, typ: any, getProps: any, key: any = '', parent: any = ''): any { | |
| 68 | + function transformPrimitive(typ: string, val: any): any { | |
| 69 | + if (typeof typ === typeof val) return val; | |
| 70 | + return invalidValue(typ, val, key, parent); | |
| 71 | + } | |
| 72 | + | |
| 73 | + function transformUnion(typs: any[], val: any): any { | |
| 74 | + // val must validate against one typ in typs | |
| 75 | + const l = typs.length; | |
| 76 | + for (let i = 0; i < l; i++) { | |
| 77 | + const typ = typs[i]; | |
| 78 | + try { | |
| 79 | + return transform(val, typ, getProps); | |
| 80 | + } catch (_) {} | |
| 81 | + } | |
| 82 | + return invalidValue(typs, val, key, parent); | |
| 83 | + } | |
| 84 | + | |
| 85 | + function transformEnum(cases: string[], val: any): any { | |
| 86 | + if (cases.indexOf(val) !== -1) return val; | |
| 87 | + return invalidValue(cases.map(a => { return l(a); }), val, key, parent); | |
| 88 | + } | |
| 89 | + | |
| 90 | + function transformArray(typ: any, val: any): any { | |
| 91 | + // val must be an array with no invalid elements | |
| 92 | + if (!Array.isArray(val)) return invalidValue(l("array"), val, key, parent); | |
| 93 | + | |
| 94 | + return val.map(el => transform(el, typ, getProps)); | |
| 95 | + } | |
| 96 | + | |
| 97 | + function transformDate(val: any): any { | |
| 98 | + if (val === null) { | |
| 99 | + return null; | |
| 100 | + } | |
| 101 | + const d = new Date(val); | |
| 102 | + if (isNaN(d.valueOf())) { | |
| 103 | + return invalidValue(l("Date"), val, key, parent); | |
| 104 | + } | |
| 105 | + return d; | |
| 106 | + } | |
| 107 | + | |
| 108 | + function transformObject(props: { [k: string]: any }, additional: any, val: any): any { | |
| 109 | + if (val === null || typeof val !== "object" || Array.isArray(val)) { | |
| 110 | + return invalidValue(l(ref || "object"), val, key, parent); | |
| 111 | + } | |
| 112 | + const result: any = {}; | |
| 113 | + Object.getOwnPropertyNames(props).forEach(key => { | |
| 114 | + const prop = props[key]; | |
| 115 | + const v = Object.prototype.hasOwnProperty.call(val, key) ? val[key] : undefined; | |
| 116 | + result[prop.key] = transform(v, prop.typ, getProps, key, ref); | |
| 117 | + }); | |
| 118 | + Object.getOwnPropertyNames(val).forEach(key => { | |
| 119 | + if (!Object.prototype.hasOwnProperty.call(props, key)) { | |
| 120 | + result[key] = transform(val[key], additional, getProps, key, ref); | |
| 121 | + } | |
| 122 | + }); | |
| 123 | + return result; | |
| 124 | + } | |
| 125 | + | |
| 126 | + if (typ === "any") return val; | |
| 127 | + if (typ === null) { | |
| 128 | + if (val === null) return val; | |
| 129 | + return invalidValue(typ, val, key, parent); | |
| 130 | + } | |
| 131 | + if (typ === false) return invalidValue(typ, val, key, parent); | |
| 132 | + let ref: any = undefined; | |
| 133 | + while (typeof typ === "object" && typ.ref !== undefined) { | |
| 134 | + ref = typ.ref; | |
| 135 | + typ = typeMap[typ.ref]; | |
| 136 | + } | |
| 137 | + if (Array.isArray(typ)) return transformEnum(typ, val); | |
| 138 | + if (typeof typ === "object") { | |
| 139 | + return typ.hasOwnProperty("pattern") ? typeof val === "string" && new RegExp(typ.pattern).test(val) ? val : invalidValue(typ, val, key, parent) | |
| 140 | + : typ.hasOwnProperty("string") ? typeof val === "string" && (typ.min === undefined || Array.from(val).length >= typ.min) && (typ.max === undefined || Array.from(val).length <= typ.max) ? transform(val, typ.string, getProps, key, parent) : invalidValue(typ, val, key, parent) | |
| 141 | + : typ.hasOwnProperty("number") ? typeof val === "number" && (typ.min === undefined || val >= typ.min) && (typ.max === undefined || val <= typ.max) ? transform(val, typ.number, getProps, key, parent) : invalidValue(typ, val, key, parent) | |
| 142 | + : typ.hasOwnProperty("integer") ? typeof val === "number" && val % 1 === 0 ? val : invalidValue(l("integer"), val, key, parent) | |
| 143 | + : typ.hasOwnProperty("unionMembers") ? transformUnion(typ.unionMembers, val) | |
| 144 | + : typ.hasOwnProperty("arrayItems") ? transformArray(typ.arrayItems, val) | |
| 145 | + : typ.hasOwnProperty("props") ? transformObject(getProps(typ), typ.additional, val) | |
| 146 | + : invalidValue(typ, val, key, parent); | |
| 147 | + } | |
| 148 | + // Numbers can be parsed by Date but shouldn't be. | |
| 149 | + if (typ === Date && (typeof val === "string" || val instanceof Date)) return transformDate(val); | |
| 150 | + return transformPrimitive(typ, val); | |
| 151 | +} | |
| 152 | + | |
| 153 | +function cast<T>(val: any, typ: any): T { | |
| 154 | + return transform(val, typ, jsonToJSProps); | |
| 155 | +} | |
| 156 | + | |
| 157 | +function uncast<T>(val: T, typ: any): any { | |
| 158 | + return transform(val, typ, jsToJSONProps); | |
| 159 | +} | |
| 160 | + | |
| 161 | +function l(typ: any) { | |
| 162 | + return { literal: typ }; | |
| 163 | +} | |
| 164 | + | |
| 165 | +function a(typ: any) { | |
| 166 | + return { arrayItems: typ }; | |
| 167 | +} | |
| 168 | + | |
| 169 | +function i(typ: any) { | |
| 170 | + return { integer: typ }; | |
| 171 | +} | |
| 172 | + | |
| 173 | +function p(pattern: any) { | |
| 174 | + return { pattern }; | |
| 175 | +} | |
| 176 | + | |
| 177 | +function s(typ: any, min: any, max: any) { | |
| 178 | + return { string: typ, min, max }; | |
| 179 | +} | |
| 180 | + | |
| 181 | +function n(typ: any, min: any, max: any) { | |
| 182 | + return { number: typ, min, max }; | |
| 183 | +} | |
| 184 | + | |
| 185 | +function u(...typs: any[]) { | |
| 186 | + return { unionMembers: typs }; | |
| 187 | +} | |
| 188 | + | |
| 189 | +function o(props: any[], additional: any) { | |
| 190 | + return { props, additional }; | |
| 191 | +} | |
| 192 | + | |
| 193 | +function m(additional: any) { | |
| 194 | + const props: any[] = []; | |
| 195 | + return { props, additional }; | |
| 196 | +} | |
| 197 | + | |
| 198 | +function r(name: string) { | |
| 199 | + return { ref: name }; | |
| 200 | +} | |
| 201 | + | |
| 202 | +const typeMap: any = { | |
| 203 | + "TopLevel": o([ | |
| 204 | + { json: "date", js: "date", typ: i(0) }, | |
| 205 | + { json: "title", js: "title", typ: "" }, | |
| 206 | + { json: "validity", js: "validity", typ: true }, | |
| 207 | + ], false), | |
| 208 | +}; | |
| 209 | + | |
| 210 | +module.exports = { | |
| 211 | + "topLevelToJson": topLevelToJson, | |
| 212 | + "toTopLevel": toTopLevel, | |
| 213 | +}; |
Test case
1 generated file · +217 −0test/inputs/schema/class-with-additional.schema
Aschema-flowreadonly-true--24da4fc107df / TopLevel.js+217 −0
| @@ -0,0 +1,217 @@ | ||
| 1 | +// @flow | |
| 2 | + | |
| 3 | +// To parse this data: | |
| 4 | +// | |
| 5 | +// const Convert = require("./TopLevel"); | |
| 6 | +// | |
| 7 | +// const topLevel = Convert.toTopLevel(json); | |
| 8 | +// | |
| 9 | +// These functions will throw an error if the JSON doesn't | |
| 10 | +// match the expected interface, even if the JSON is valid. | |
| 11 | + | |
| 12 | +export type TopLevel = { | |
| 13 | + +map?: Map; | |
| 14 | +}; | |
| 15 | + | |
| 16 | +export type Map = { | |
| 17 | + +foo?: number; | |
| 18 | + +[property: string]: boolean | number | void; | |
| 19 | +}; | |
| 20 | + | |
| 21 | +// Converts JSON strings to/from your types | |
| 22 | +// and asserts the results of JSON.parse at runtime | |
| 23 | +function toTopLevel(json: string): TopLevel { | |
| 24 | + return cast(JSON.parse(json), r("TopLevel")); | |
| 25 | +} | |
| 26 | + | |
| 27 | +function topLevelToJson(value: TopLevel): string { | |
| 28 | + return JSON.stringify(uncast(value, r("TopLevel")), null, 2); | |
| 29 | +} | |
| 30 | + | |
| 31 | +function invalidValue(typ: any, val: any, key: any, parent: any = '') { | |
| 32 | + const prettyTyp = prettyTypeName(typ); | |
| 33 | + const parentText = parent ? ` on ${parent}` : ''; | |
| 34 | + const keyText = key ? ` for key "${key}"` : ''; | |
| 35 | + throw Error(`Invalid value${keyText}${parentText}. Expected ${prettyTyp} but got ${JSON.stringify(val)}`); | |
| 36 | +} | |
| 37 | + | |
| 38 | +function prettyTypeName(typ: any): string { | |
| 39 | + if (Array.isArray(typ)) { | |
| 40 | + if (typ.length === 2 && typ[0] === undefined) { | |
| 41 | + return `an optional ${prettyTypeName(typ[1])}`; | |
| 42 | + } else { | |
| 43 | + return `one of [${typ.map(a => { return prettyTypeName(a); }).join(", ")}]`; | |
| 44 | + } | |
| 45 | + } else if (typeof typ === "object" && typ.literal !== undefined) { | |
| 46 | + return typ.literal; | |
| 47 | + } else { | |
| 48 | + return typeof typ; | |
| 49 | + } | |
| 50 | +} | |
| 51 | + | |
| 52 | +function jsonToJSProps(typ: any): any { | |
| 53 | + if (typ.jsonToJS === undefined) { | |
| 54 | + const map: any = {}; | |
| 55 | + typ.props.forEach((p: any) => map[p.json] = { key: p.js, typ: p.typ }); | |
| 56 | + typ.jsonToJS = map; | |
| 57 | + } | |
| 58 | + return typ.jsonToJS; | |
| 59 | +} | |
| 60 | + | |
| 61 | +function jsToJSONProps(typ: any): any { | |
| 62 | + if (typ.jsToJSON === undefined) { | |
| 63 | + const map: any = {}; | |
| 64 | + typ.props.forEach((p: any) => map[p.js] = { key: p.json, typ: p.typ }); | |
| 65 | + typ.jsToJSON = map; | |
| 66 | + } | |
| 67 | + return typ.jsToJSON; | |
| 68 | +} | |
| 69 | + | |
| 70 | +function transform(val: any, typ: any, getProps: any, key: any = '', parent: any = ''): any { | |
| 71 | + function transformPrimitive(typ: string, val: any): any { | |
| 72 | + if (typeof typ === typeof val) return val; | |
| 73 | + return invalidValue(typ, val, key, parent); | |
| 74 | + } | |
| 75 | + | |
| 76 | + function transformUnion(typs: any[], val: any): any { | |
| 77 | + // val must validate against one typ in typs | |
| 78 | + const l = typs.length; | |
| 79 | + for (let i = 0; i < l; i++) { | |
| 80 | + const typ = typs[i]; | |
| 81 | + try { | |
| 82 | + return transform(val, typ, getProps); | |
| 83 | + } catch (_) {} | |
| 84 | + } | |
| 85 | + return invalidValue(typs, val, key, parent); | |
| 86 | + } | |
| 87 | + | |
| 88 | + function transformEnum(cases: string[], val: any): any { | |
| 89 | + if (cases.indexOf(val) !== -1) return val; | |
| 90 | + return invalidValue(cases.map(a => { return l(a); }), val, key, parent); | |
| 91 | + } | |
| 92 | + | |
| 93 | + function transformArray(typ: any, val: any): any { | |
| 94 | + // val must be an array with no invalid elements | |
| 95 | + if (!Array.isArray(val)) return invalidValue(l("array"), val, key, parent); | |
| 96 | + | |
| 97 | + return val.map(el => transform(el, typ, getProps)); | |
| 98 | + } | |
| 99 | + | |
| 100 | + function transformDate(val: any): any { | |
| 101 | + if (val === null) { | |
| 102 | + return null; | |
| 103 | + } | |
| 104 | + const d = new Date(val); | |
| 105 | + if (isNaN(d.valueOf())) { | |
| 106 | + return invalidValue(l("Date"), val, key, parent); | |
| 107 | + } | |
| 108 | + return d; | |
| 109 | + } | |
| 110 | + | |
| 111 | + function transformObject(props: { [k: string]: any }, additional: any, val: any): any { | |
| 112 | + if (val === null || typeof val !== "object" || Array.isArray(val)) { | |
| 113 | + return invalidValue(l(ref || "object"), val, key, parent); | |
| 114 | + } | |
| 115 | + const result: any = {}; | |
| 116 | + Object.getOwnPropertyNames(props).forEach(key => { | |
| 117 | + const prop = props[key]; | |
| 118 | + const v = Object.prototype.hasOwnProperty.call(val, key) ? val[key] : undefined; | |
| 119 | + result[prop.key] = transform(v, prop.typ, getProps, key, ref); | |
| 120 | + }); | |
| 121 | + Object.getOwnPropertyNames(val).forEach(key => { | |
| 122 | + if (!Object.prototype.hasOwnProperty.call(props, key)) { | |
| 123 | + result[key] = transform(val[key], additional, getProps, key, ref); | |
| 124 | + } | |
| 125 | + }); | |
| 126 | + return result; | |
| 127 | + } | |
| 128 | + | |
| 129 | + if (typ === "any") return val; | |
| 130 | + if (typ === null) { | |
| 131 | + if (val === null) return val; | |
| 132 | + return invalidValue(typ, val, key, parent); | |
| 133 | + } | |
| 134 | + if (typ === false) return invalidValue(typ, val, key, parent); | |
| 135 | + let ref: any = undefined; | |
| 136 | + while (typeof typ === "object" && typ.ref !== undefined) { | |
| 137 | + ref = typ.ref; | |
| 138 | + typ = typeMap[typ.ref]; | |
| 139 | + } | |
| 140 | + if (Array.isArray(typ)) return transformEnum(typ, val); | |
| 141 | + if (typeof typ === "object") { | |
| 142 | + return typ.hasOwnProperty("pattern") ? typeof val === "string" && new RegExp(typ.pattern).test(val) ? val : invalidValue(typ, val, key, parent) | |
| 143 | + : typ.hasOwnProperty("string") ? typeof val === "string" && (typ.min === undefined || Array.from(val).length >= typ.min) && (typ.max === undefined || Array.from(val).length <= typ.max) ? transform(val, typ.string, getProps, key, parent) : invalidValue(typ, val, key, parent) | |
| 144 | + : typ.hasOwnProperty("number") ? typeof val === "number" && (typ.min === undefined || val >= typ.min) && (typ.max === undefined || val <= typ.max) ? transform(val, typ.number, getProps, key, parent) : invalidValue(typ, val, key, parent) | |
| 145 | + : typ.hasOwnProperty("integer") ? typeof val === "number" && val % 1 === 0 ? val : invalidValue(l("integer"), val, key, parent) | |
| 146 | + : typ.hasOwnProperty("unionMembers") ? transformUnion(typ.unionMembers, val) | |
| 147 | + : typ.hasOwnProperty("arrayItems") ? transformArray(typ.arrayItems, val) | |
| 148 | + : typ.hasOwnProperty("props") ? transformObject(getProps(typ), typ.additional, val) | |
| 149 | + : invalidValue(typ, val, key, parent); | |
| 150 | + } | |
| 151 | + // Numbers can be parsed by Date but shouldn't be. | |
| 152 | + if (typ === Date && (typeof val === "string" || val instanceof Date)) return transformDate(val); | |
| 153 | + return transformPrimitive(typ, val); | |
| 154 | +} | |
| 155 | + | |
| 156 | +function cast<T>(val: any, typ: any): T { | |
| 157 | + return transform(val, typ, jsonToJSProps); | |
| 158 | +} | |
| 159 | + | |
| 160 | +function uncast<T>(val: T, typ: any): any { | |
| 161 | + return transform(val, typ, jsToJSONProps); | |
| 162 | +} | |
| 163 | + | |
| 164 | +function l(typ: any) { | |
| 165 | + return { literal: typ }; | |
| 166 | +} | |
| 167 | + | |
| 168 | +function a(typ: any) { | |
| 169 | + return { arrayItems: typ }; | |
| 170 | +} | |
| 171 | + | |
| 172 | +function i(typ: any) { | |
| 173 | + return { integer: typ }; | |
| 174 | +} | |
| 175 | + | |
| 176 | +function p(pattern: any) { | |
| 177 | + return { pattern }; | |
| 178 | +} | |
| 179 | + | |
| 180 | +function s(typ: any, min: any, max: any) { | |
| 181 | + return { string: typ, min, max }; | |
| 182 | +} | |
| 183 | + | |
| 184 | +function n(typ: any, min: any, max: any) { | |
| 185 | + return { number: typ, min, max }; | |
| 186 | +} | |
| 187 | + | |
| 188 | +function u(...typs: any[]) { | |
| 189 | + return { unionMembers: typs }; | |
| 190 | +} | |
| 191 | + | |
| 192 | +function o(props: any[], additional: any) { | |
| 193 | + return { props, additional }; | |
| 194 | +} | |
| 195 | + | |
| 196 | +function m(additional: any) { | |
| 197 | + const props: any[] = []; | |
| 198 | + return { props, additional }; | |
| 199 | +} | |
| 200 | + | |
| 201 | +function r(name: string) { | |
| 202 | + return { ref: name }; | |
| 203 | +} | |
| 204 | + | |
| 205 | +const typeMap: any = { | |
| 206 | + "TopLevel": o([ | |
| 207 | + { json: "map", js: "map", typ: u(undefined, r("Map")) }, | |
| 208 | + ], false), | |
| 209 | + "Map": o([ | |
| 210 | + { json: "foo", js: "foo", typ: u(undefined, 3.14) }, | |
| 211 | + ], true), | |
| 212 | +}; | |
| 213 | + | |
| 214 | +module.exports = { | |
| 215 | + "topLevelToJson": topLevelToJson, | |
| 216 | + "toTopLevel": toTopLevel, | |
| 217 | +}; |
No generated files match these filters.