| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219 | 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();
 |