You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

generateNodeUtils.js 5.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. const definitions = require("../src/definitions");
  2. const flatMap = require("array.prototype.flatmap");
  3. const {
  4. typeSignature,
  5. iterateProps,
  6. mapProps,
  7. filterProps,
  8. unique
  9. } = require("./util");
  10. const stdout = process.stdout;
  11. const jsTypes = ["string", "number", "boolean"];
  12. const quote = value => `"${value}"`;
  13. function params(fields) {
  14. const optionalDefault = field => (field.default ? ` = ${field.default}` : "");
  15. return mapProps(fields)
  16. .map(field => `${typeSignature(field)}${optionalDefault(field)}`)
  17. .join(",");
  18. }
  19. function assertParamType({ assertNodeType, array, name, type }) {
  20. if (array) {
  21. // TODO - assert contents of array?
  22. return `assert(typeof ${name} === "object" && typeof ${name}.length !== "undefined")\n`;
  23. } else {
  24. if (jsTypes.includes(type)) {
  25. return `assert(
  26. typeof ${name} === "${type}",
  27. "Argument ${name} must be of type ${type}, given: " + typeof ${name}
  28. )`;
  29. }
  30. if (assertNodeType === true) {
  31. return `assert(
  32. ${name}.type === "${type}",
  33. "Argument ${name} must be of type ${type}, given: " + ${name}.type
  34. )`;
  35. }
  36. return "";
  37. }
  38. }
  39. function assertParam(meta) {
  40. const paramAssertion = assertParamType(meta);
  41. if (paramAssertion === "") {
  42. return "";
  43. }
  44. if (meta.maybe || meta.optional) {
  45. return `
  46. if (${meta.name} !== null && ${meta.name} !== undefined) {
  47. ${paramAssertion};
  48. }
  49. `;
  50. } else {
  51. return paramAssertion;
  52. }
  53. }
  54. function assertParams(fields) {
  55. return mapProps(fields)
  56. .map(assertParam)
  57. .join("\n");
  58. }
  59. function buildObject(typeDef) {
  60. const optionalField = meta => {
  61. if (meta.array) {
  62. // omit optional array properties if the constructor function was supplied
  63. // with an empty array
  64. return `
  65. if (typeof ${meta.name} !== "undefined" && ${meta.name}.length > 0) {
  66. node.${meta.name} = ${meta.name};
  67. }
  68. `;
  69. } else if (meta.type === "Object") {
  70. // omit optional object properties if they have no keys
  71. return `
  72. if (typeof ${meta.name} !== "undefined" && Object.keys(${
  73. meta.name
  74. }).length !== 0) {
  75. node.${meta.name} = ${meta.name};
  76. }
  77. `;
  78. } else if (meta.type === "boolean") {
  79. // omit optional boolean properties if they are not true
  80. return `
  81. if (${meta.name} === true) {
  82. node.${meta.name} = true;
  83. }
  84. `;
  85. } else {
  86. return `
  87. if (typeof ${meta.name} !== "undefined") {
  88. node.${meta.name} = ${meta.name};
  89. }
  90. `;
  91. }
  92. };
  93. const fields = mapProps(typeDef.fields)
  94. .filter(f => !f.optional && !f.constant)
  95. .map(f => f.name);
  96. const constants = mapProps(typeDef.fields)
  97. .filter(f => f.constant)
  98. .map(f => `${f.name}: "${f.value}"`);
  99. return `
  100. const node: ${typeDef.flowTypeName || typeDef.name} = {
  101. type: "${typeDef.name}",
  102. ${constants.concat(fields).join(",")}
  103. }
  104. ${mapProps(typeDef.fields)
  105. .filter(f => f.optional)
  106. .map(optionalField)
  107. .join("")}
  108. `;
  109. }
  110. function lowerCamelCase(name) {
  111. return name.substring(0, 1).toLowerCase() + name.substring(1);
  112. }
  113. function generate() {
  114. stdout.write(`
  115. // @flow
  116. // THIS FILE IS AUTOGENERATED
  117. // see scripts/generateNodeUtils.js
  118. import { assert } from "mamacro";
  119. function isTypeOf(t: string) {
  120. return (n: Node) => n.type === t;
  121. }
  122. function assertTypeOf(t: string) {
  123. return (n: Node) => assert(n.type === t);
  124. }
  125. `);
  126. // Node builders
  127. iterateProps(definitions, typeDefinition => {
  128. stdout.write(`
  129. export function ${lowerCamelCase(typeDefinition.name)} (
  130. ${params(filterProps(typeDefinition.fields, f => !f.constant))}
  131. ): ${typeDefinition.name} {
  132. ${assertParams(filterProps(typeDefinition.fields, f => !f.constant))}
  133. ${buildObject(typeDefinition)}
  134. return node;
  135. }
  136. `);
  137. });
  138. // Node testers
  139. iterateProps(definitions, typeDefinition => {
  140. stdout.write(`
  141. export const is${typeDefinition.name} =
  142. isTypeOf("${typeDefinition.name}");
  143. `);
  144. });
  145. // Node union type testers
  146. const unionTypes = unique(
  147. flatMap(mapProps(definitions).filter(d => d.unionType), d => d.unionType)
  148. );
  149. unionTypes.forEach(unionType => {
  150. stdout.write(
  151. `
  152. export const is${unionType} = (node: Node) => ` +
  153. mapProps(definitions)
  154. .filter(d => d.unionType && d.unionType.includes(unionType))
  155. .map(d => `is${d.name}(node) `)
  156. .join("||") +
  157. ";\n\n"
  158. );
  159. });
  160. // Node assertion
  161. iterateProps(definitions, typeDefinition => {
  162. stdout.write(`
  163. export const assert${typeDefinition.name} =
  164. assertTypeOf("${typeDefinition.name}");
  165. `);
  166. });
  167. // a map from node type to its set of union types
  168. stdout.write(
  169. `
  170. export const unionTypesMap = {` +
  171. mapProps(definitions)
  172. .filter(d => d.unionType)
  173. .map(t => `"${t.name}": [${t.unionType.map(quote).join(",")}]\n`) +
  174. `};
  175. `
  176. );
  177. // an array of all node and union types
  178. stdout.write(
  179. `
  180. export const nodeAndUnionTypes = [` +
  181. mapProps(definitions)
  182. .map(t => `"${t.name}"`)
  183. .concat(unionTypes.map(quote))
  184. .join(",") +
  185. `];`
  186. );
  187. }
  188. generate();