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