1 |
3a515b92
|
cagy
|
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();
|