Projekt

Obecné

Profil

Stáhnout (5.23 KB) Statistiky
| Větev: | Revize:
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

    
11
const stdout = process.stdout;
12

    
13
const jsTypes = ["string", "number", "boolean"];
14

    
15
const quote = value => `"${value}"`;
16

    
17
function params(fields) {
18
  const optionalDefault = field => (field.default ? ` = ${field.default}` : "");
19
  return mapProps(fields)
20
    .map(field => `${typeSignature(field)}${optionalDefault(field)}`)
21
    .join(",");
22
}
23

    
24
function assertParamType({ assertNodeType, array, name, type }) {
25
  if (array) {
26
    // TODO - assert contents of array?
27
    return `assert(typeof ${name} === "object" && typeof ${name}.length !== "undefined")\n`;
28
  } else {
29
    if (jsTypes.includes(type)) {
30
      return `assert(
31
          typeof ${name} === "${type}",
32
          "Argument ${name} must be of type ${type}, given: " + typeof ${name}
33
      )`;
34
    }
35

    
36
    if (assertNodeType === true) {
37
      return `assert(
38
        ${name}.type === "${type}",
39
        "Argument ${name} must be of type ${type}, given: " + ${name}.type
40
      )`;
41
    }
42

    
43
    return "";
44
  }
45
}
46

    
47
function assertParam(meta) {
48
  const paramAssertion = assertParamType(meta);
49

    
50
  if (paramAssertion === "") {
51
    return "";
52
  }
53

    
54
  if (meta.maybe || meta.optional) {
55
    return `
56
      if (${meta.name} !== null && ${meta.name} !== undefined) {
57
        ${paramAssertion};
58
      }
59
    `;
60
  } else {
61
    return paramAssertion;
62
  }
63
}
64

    
65
function assertParams(fields) {
66
  return mapProps(fields)
67
    .map(assertParam)
68
    .join("\n");
69
}
70

    
71
function buildObject(typeDef) {
72
  const optionalField = meta => {
73
    if (meta.array) {
74
      // omit optional array properties if the constructor function was supplied
75
      // with an empty array
76
      return `
77
        if (typeof ${meta.name} !== "undefined" && ${meta.name}.length > 0) {
78
          node.${meta.name} = ${meta.name};
79
        }
80
      `;
81
    } else if (meta.type === "Object") {
82
      // omit optional object properties if they have no keys
83
      return `
84
        if (typeof ${meta.name} !== "undefined" && Object.keys(${
85
        meta.name
86
      }).length !== 0) {
87
          node.${meta.name} = ${meta.name};
88
        }
89
      `;
90
    } else if (meta.type === "boolean") {
91
      // omit optional boolean properties if they are not true
92
      return `
93
        if (${meta.name} === true) {
94
          node.${meta.name} = true;
95
        }
96
      `;
97
    } else {
98
      return `
99
        if (typeof ${meta.name} !== "undefined") {
100
          node.${meta.name} = ${meta.name};
101
        }
102
      `;
103
    }
104
  };
105

    
106
  const fields = mapProps(typeDef.fields)
107
    .filter(f => !f.optional && !f.constant)
108
    .map(f => f.name);
109

    
110
  const constants = mapProps(typeDef.fields)
111
    .filter(f => f.constant)
112
    .map(f => `${f.name}: "${f.value}"`);
113

    
114
  return `
115
    const node: ${typeDef.flowTypeName || typeDef.name} = {
116
      type: "${typeDef.name}",
117
      ${constants.concat(fields).join(",")}
118
    }
119

    
120
    ${mapProps(typeDef.fields)
121
      .filter(f => f.optional)
122
      .map(optionalField)
123
      .join("")}
124
  `;
125
}
126

    
127
function lowerCamelCase(name) {
128
  return name.substring(0, 1).toLowerCase() + name.substring(1);
129
}
130

    
131
function generate() {
132
  stdout.write(`
133
    // @flow
134

    
135
    // THIS FILE IS AUTOGENERATED
136
    // see scripts/generateNodeUtils.js
137

    
138
    import { assert } from "mamacro";
139

    
140
    function isTypeOf(t: string) {
141
      return (n: Node) => n.type === t;
142
    }
143

    
144
    function assertTypeOf(t: string) {
145
      return (n: Node) => assert(n.type === t);
146
    }
147
  `);
148

    
149
  // Node builders
150
  iterateProps(definitions, typeDefinition => {
151
    stdout.write(`
152
      export function ${lowerCamelCase(typeDefinition.name)} (
153
        ${params(filterProps(typeDefinition.fields, f => !f.constant))}
154
      ): ${typeDefinition.name} {
155

    
156
        ${assertParams(filterProps(typeDefinition.fields, f => !f.constant))}
157
        ${buildObject(typeDefinition)} 
158

    
159
        return node;
160
      }
161
    `);
162
  });
163

    
164
  // Node testers
165
  iterateProps(definitions, typeDefinition => {
166
    stdout.write(`
167
      export const is${typeDefinition.name} =
168
        isTypeOf("${typeDefinition.name}");
169
    `);
170
  });
171

    
172
  // Node union type testers
173
  const unionTypes = unique(
174
    flatMap(mapProps(definitions).filter(d => d.unionType), d => d.unionType)
175
  );
176
  unionTypes.forEach(unionType => {
177
    stdout.write(
178
      `
179
      export const is${unionType} = (node: Node) => ` +
180
        mapProps(definitions)
181
          .filter(d => d.unionType && d.unionType.includes(unionType))
182
          .map(d => `is${d.name}(node) `)
183
          .join("||") +
184
        ";\n\n"
185
    );
186
  });
187

    
188
  // Node assertion
189
  iterateProps(definitions, typeDefinition => {
190
    stdout.write(`
191
      export const assert${typeDefinition.name} =
192
        assertTypeOf("${typeDefinition.name}");
193
    `);
194
  });
195

    
196
  // a map from node type to its set of union types
197
  stdout.write(
198
    `
199
    export const unionTypesMap = {` +
200
      mapProps(definitions)
201
        .filter(d => d.unionType)
202
        .map(t => `"${t.name}": [${t.unionType.map(quote).join(",")}]\n`) +
203
      `};
204
      `
205
  );
206

    
207
  // an array of all node and union types
208
  stdout.write(
209
    `
210
    export const nodeAndUnionTypes = [` +
211
      mapProps(definitions)
212
        .map(t => `"${t.name}"`)
213
        .concat(unionTypes.map(quote))
214
        .join(",") +
215
      `];`
216
  );
217
}
218

    
219
generate();
(1-1/3)