Spaces:
Sleeping
Sleeping
| const definitions = require("../src/definitions"); | |
| const flatMap = require("array.prototype.flatmap"); | |
| const { | |
| typeSignature, | |
| iterateProps, | |
| mapProps, | |
| filterProps, | |
| unique | |
| } = require("./util"); | |
| const stdout = process.stdout; | |
| const jsTypes = ["string", "number", "boolean"]; | |
| const quote = value => `"${value}"`; | |
| function params(fields) { | |
| const optionalDefault = field => (field.default ? ` = ${field.default}` : ""); | |
| return mapProps(fields) | |
| .map(field => `${typeSignature(field)}${optionalDefault(field)}`) | |
| .join(","); | |
| } | |
| function assertParamType({ assertNodeType, array, name, type }) { | |
| if (array) { | |
| // TODO - assert contents of array? | |
| return `assert(typeof ${name} === "object" && typeof ${name}.length !== "undefined")\n`; | |
| } else { | |
| if (jsTypes.includes(type)) { | |
| return `assert( | |
| typeof ${name} === "${type}", | |
| "Argument ${name} must be of type ${type}, given: " + typeof ${name} | |
| )`; | |
| } | |
| if (assertNodeType === true) { | |
| return `assert( | |
| ${name}.type === "${type}", | |
| "Argument ${name} must be of type ${type}, given: " + ${name}.type | |
| )`; | |
| } | |
| return ""; | |
| } | |
| } | |
| function assertParam(meta) { | |
| const paramAssertion = assertParamType(meta); | |
| if (paramAssertion === "") { | |
| return ""; | |
| } | |
| if (meta.maybe || meta.optional) { | |
| return ` | |
| if (${meta.name} !== null && ${meta.name} !== undefined) { | |
| ${paramAssertion}; | |
| } | |
| `; | |
| } else { | |
| return paramAssertion; | |
| } | |
| } | |
| function assertParams(fields) { | |
| return mapProps(fields) | |
| .map(assertParam) | |
| .join("\n"); | |
| } | |
| function buildObject(typeDef) { | |
| const optionalField = meta => { | |
| if (meta.array) { | |
| // omit optional array properties if the constructor function was supplied | |
| // with an empty array | |
| return ` | |
| if (typeof ${meta.name} !== "undefined" && ${meta.name}.length > 0) { | |
| node.${meta.name} = ${meta.name}; | |
| } | |
| `; | |
| } else if (meta.type === "Object") { | |
| // omit optional object properties if they have no keys | |
| return ` | |
| if (typeof ${meta.name} !== "undefined" && Object.keys(${ | |
| meta.name | |
| }).length !== 0) { | |
| node.${meta.name} = ${meta.name}; | |
| } | |
| `; | |
| } else if (meta.type === "boolean") { | |
| // omit optional boolean properties if they are not true | |
| return ` | |
| if (${meta.name} === true) { | |
| node.${meta.name} = true; | |
| } | |
| `; | |
| } else { | |
| return ` | |
| if (typeof ${meta.name} !== "undefined") { | |
| node.${meta.name} = ${meta.name}; | |
| } | |
| `; | |
| } | |
| }; | |
| const fields = mapProps(typeDef.fields) | |
| .filter(f => !f.optional && !f.constant) | |
| .map(f => f.name); | |
| const constants = mapProps(typeDef.fields) | |
| .filter(f => f.constant) | |
| .map(f => `${f.name}: "${f.value}"`); | |
| return ` | |
| const node: ${typeDef.flowTypeName || typeDef.name} = { | |
| type: "${typeDef.name}", | |
| ${constants.concat(fields).join(",")} | |
| } | |
| ${mapProps(typeDef.fields) | |
| .filter(f => f.optional) | |
| .map(optionalField) | |
| .join("")} | |
| `; | |
| } | |
| function lowerCamelCase(name) { | |
| return name.substring(0, 1).toLowerCase() + name.substring(1); | |
| } | |
| function generate() { | |
| stdout.write(` | |
| // @flow | |
| // THIS FILE IS AUTOGENERATED | |
| // see scripts/generateNodeUtils.js | |
| import { assert } from "mamacro"; | |
| function isTypeOf(t: string) { | |
| return (n: Node) => n.type === t; | |
| } | |
| function assertTypeOf(t: string) { | |
| return (n: Node) => assert(n.type === t); | |
| } | |
| `); | |
| // Node builders | |
| iterateProps(definitions, typeDefinition => { | |
| stdout.write(` | |
| export function ${lowerCamelCase(typeDefinition.name)} ( | |
| ${params(filterProps(typeDefinition.fields, f => !f.constant))} | |
| ): ${typeDefinition.name} { | |
| ${assertParams(filterProps(typeDefinition.fields, f => !f.constant))} | |
| ${buildObject(typeDefinition)} | |
| return node; | |
| } | |
| `); | |
| }); | |
| // Node testers | |
| iterateProps(definitions, typeDefinition => { | |
| stdout.write(` | |
| export const is${typeDefinition.name} = | |
| isTypeOf("${typeDefinition.name}"); | |
| `); | |
| }); | |
| // Node union type testers | |
| const unionTypes = unique( | |
| flatMap(mapProps(definitions).filter(d => d.unionType), d => d.unionType) | |
| ); | |
| unionTypes.forEach(unionType => { | |
| stdout.write( | |
| ` | |
| export const is${unionType} = (node: Node) => ` + | |
| mapProps(definitions) | |
| .filter(d => d.unionType && d.unionType.includes(unionType)) | |
| .map(d => `is${d.name}(node) `) | |
| .join("||") + | |
| ";\n\n" | |
| ); | |
| }); | |
| // Node assertion | |
| iterateProps(definitions, typeDefinition => { | |
| stdout.write(` | |
| export const assert${typeDefinition.name} = | |
| assertTypeOf("${typeDefinition.name}"); | |
| `); | |
| }); | |
| // a map from node type to its set of union types | |
| stdout.write( | |
| ` | |
| export const unionTypesMap = {` + | |
| mapProps(definitions) | |
| .filter(d => d.unionType) | |
| .map(t => `"${t.name}": [${t.unionType.map(quote).join(",")}]\n`) + | |
| `}; | |
| ` | |
| ); | |
| // an array of all node and union types | |
| stdout.write( | |
| ` | |
| export const nodeAndUnionTypes = [` + | |
| mapProps(definitions) | |
| .map(t => `"${t.name}"`) | |
| .concat(unionTypes.map(quote)) | |
| .join(",") + | |
| `];` | |
| ); | |
| } | |
| generate(); | |