4fd28b64063938ae13f3110deb7ac84a5a219920.svn-base
5.31 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
"use strict";
const definitions = require("../../lib/definitions");
const formatBuilderName = require("../utils/formatBuilderName");
const lowerFirst = require("../utils/lowerFirst");
const t = require("../../");
const stringifyValidator = require("../utils/stringifyValidator");
function areAllRemainingFieldsNullable(fieldName, fieldNames, fields) {
const index = fieldNames.indexOf(fieldName);
return fieldNames.slice(index).every(_ => isNullable(fields[_]));
}
function hasDefault(field) {
return field.default != null;
}
function isNullable(field) {
return field.optional || hasDefault(field);
}
function sortFieldNames(fields, type) {
return fields.sort((fieldA, fieldB) => {
const indexA = t.BUILDER_KEYS[type].indexOf(fieldA);
const indexB = t.BUILDER_KEYS[type].indexOf(fieldB);
if (indexA === indexB) return fieldA < fieldB ? -1 : 1;
if (indexA === -1) return 1;
if (indexB === -1) return -1;
return indexA - indexB;
});
}
function generateBuilderArgs(type) {
const fields = t.NODE_FIELDS[type];
const fieldNames = sortFieldNames(Object.keys(t.NODE_FIELDS[type]), type);
const builderNames = t.BUILDER_KEYS[type];
const args = [];
fieldNames.forEach(fieldName => {
const field = fields[fieldName];
// Future / annoying TODO:
// MemberExpression.property, ObjectProperty.key and ObjectMethod.key need special cases; either:
// - convert the declaration to chain() like ClassProperty.key and ClassMethod.key,
// - declare an alias type for valid keys, detect the case and reuse it here,
// - declare a disjoint union with, for example, ObjectPropertyBase,
// ObjectPropertyLiteralKey and ObjectPropertyComputedKey, and declare ObjectProperty
// as "ObjectPropertyBase & (ObjectPropertyLiteralKey | ObjectPropertyComputedKey)"
let typeAnnotation = stringifyValidator(field.validate, "t.");
if (isNullable(field) && !hasDefault(field)) {
typeAnnotation += " | null";
}
if (builderNames.includes(fieldName)) {
const bindingIdentifierName = t.toBindingIdentifierName(fieldName);
if (areAllRemainingFieldsNullable(fieldName, builderNames, fields)) {
args.push(
`${bindingIdentifierName}${
isNullable(field) ? "?:" : ":"
} ${typeAnnotation}`
);
} else {
args.push(
`${bindingIdentifierName}: ${typeAnnotation}${
isNullable(field) ? " | undefined" : ""
}`
);
}
}
});
return args;
}
module.exports = function generateBuilders(kind) {
return kind === "uppercase.js"
? generateUppercaseBuilders()
: generateLowercaseBuilders();
};
function generateLowercaseBuilders() {
let output = `/*
* This file is auto-generated! Do not modify it directly.
* To re-generate run 'make build'
*/
import builder from "../builder";
import type * as t from "../..";
/* eslint-disable @typescript-eslint/no-unused-vars */
`;
const reservedNames = new Set(["super", "import"]);
Object.keys(definitions.BUILDER_KEYS).forEach(type => {
const defArgs = generateBuilderArgs(type);
const formatedBuilderName = formatBuilderName(type);
const formatedBuilderNameLocal = reservedNames.has(formatedBuilderName)
? `_${formatedBuilderName}`
: formatedBuilderName;
output += `${
formatedBuilderNameLocal === formatedBuilderName ? "export " : ""
}function ${formatedBuilderNameLocal}(${defArgs.join(
", "
)}): t.${type} { return builder("${type}", ...arguments); }\n`;
if (formatedBuilderNameLocal !== formatedBuilderName) {
output += `export { ${formatedBuilderNameLocal} as ${formatedBuilderName} };\n`;
}
// This is needed for backwards compatibility.
// It should be removed in the next major version.
// JSXIdentifier -> jSXIdentifier
if (/^[A-Z]{2}/.test(type)) {
output += `export { ${formatedBuilderNameLocal} as ${lowerFirst(
type
)} }\n`;
}
});
Object.keys(definitions.DEPRECATED_KEYS).forEach(type => {
const newType = definitions.DEPRECATED_KEYS[type];
const formatedBuilderName = formatBuilderName(type);
output += `/** @deprecated */
function ${type}(...args: Array<any>): any {
console.trace("The node type ${type} has been renamed to ${newType}");
return builder("${type}", ...args);
}
export { ${type} as ${formatedBuilderName} };\n`;
// This is needed for backwards compatibility.
// It should be removed in the next major version.
// JSXIdentifier -> jSXIdentifier
if (/^[A-Z]{2}/.test(type)) {
output += `export { ${type} as ${lowerFirst(type)} }\n`;
}
});
return output;
}
function generateUppercaseBuilders() {
let output = `/*
* This file is auto-generated! Do not modify it directly.
* To re-generate run 'make build'
*/
/**
* This file is written in JavaScript and not TypeScript because uppercase builders
* conflict with AST types. TypeScript reads the uppercase.d.ts file instead.
*/
export {\n`;
Object.keys(definitions.BUILDER_KEYS).forEach(type => {
const formatedBuilderName = formatBuilderName(type);
output += ` ${formatedBuilderName} as ${type},\n`;
});
Object.keys(definitions.DEPRECATED_KEYS).forEach(type => {
const formatedBuilderName = formatBuilderName(type);
output += ` ${formatedBuilderName} as ${type},\n`;
});
output += ` } from './index';\n`;
return output;
}