UNPKG

4.17 kBJavaScriptView Raw
1const generate = require('@babel/generator').default;
2
3const namedTypes = {
4 NumberTypeAnnotation: 'number',
5 BooleanTypeAnnotation: 'boolean',
6 StringTypeAnnotation: 'string'
7};
8
9const oneToOne = {
10 AnyTypeAnnotation: 'AllLiteral',
11 MixedTypeAnnotation: 'AllLiteral',
12 NullLiteralTypeAnnotation: 'NullLiteral',
13 VoidTypeAnnotation: 'VoidLiteral'
14};
15
16function propertyToField(property) {
17 if (!property.value) return null;
18
19 let type = flowDoctrine(property.value);
20 if (property.optional) {
21 // Doctrine does not support optional fields but it does have something called optional types
22 // (which makes no sense, but let's play along).
23 type = {
24 type: 'OptionalType',
25 expression: type
26 };
27 }
28 return {
29 type: 'FieldType',
30 key: property.key.name || property.key.value,
31 value: type
32 };
33}
34
35/**
36 * Babel parses Flow annotations in JavaScript into AST nodes. documentation.js uses
37 * Babel to parse JavaScript. This method restructures those Babel-generated
38 * objects into objects that fit the output of Doctrine, the module we use
39 * to parse JSDoc annotations. This lets us use Flow annotations _as_
40 * JSDoc annotations.
41 *
42 * @private
43 * @param {Object} type babel-parsed flow type
44 * @returns {Object} doctrine compatible type
45 */
46function flowDoctrine(type) {
47 if (type.type in namedTypes) {
48 const doctrineType = {
49 type: 'NameExpression',
50 name: namedTypes[type.type]
51 };
52 return doctrineType;
53 }
54
55 if (type.type in oneToOne) {
56 return { type: oneToOne[type.type] };
57 }
58
59 switch (type.type) {
60 case 'NullableTypeAnnotation':
61 return {
62 type: 'NullableType',
63 expression: flowDoctrine(type.typeAnnotation)
64 };
65 case 'UnionTypeAnnotation':
66 return {
67 type: 'UnionType',
68 elements: type.types.map(flowDoctrine)
69 };
70 // [number]
71 // [string, boolean, number]
72 case 'TupleTypeAnnotation':
73 return {
74 type: 'ArrayType',
75 elements: type.types.map(flowDoctrine)
76 };
77 // number[]
78 case 'ArrayTypeAnnotation':
79 return {
80 type: 'TypeApplication',
81 expression: {
82 type: 'NameExpression',
83 name: 'Array'
84 },
85 applications: [flowDoctrine(type.elementType)]
86 };
87 // (y: number) => bool
88 case 'FunctionTypeAnnotation':
89 return {
90 type: 'FunctionType',
91 params: type.params.map(param => {
92 let name = '';
93 if (param.name && param.name.name) {
94 name = param.name.name;
95 }
96 return {
97 type: 'ParameterType',
98 name,
99 expression: flowDoctrine(param.typeAnnotation)
100 };
101 }),
102 result: flowDoctrine(type.returnType)
103 };
104
105 case 'GenericTypeAnnotation':
106 if (type.typeParameters) {
107 return {
108 type: 'TypeApplication',
109 expression: {
110 type: 'NameExpression',
111 name: generate(type.id, {
112 compact: true
113 }).code
114 },
115 applications: type.typeParameters.params.map(flowDoctrine)
116 };
117 }
118
119 return {
120 type: 'NameExpression',
121 name: generate(type.id, {
122 compact: true
123 }).code
124 };
125
126 case 'ObjectTypeAnnotation':
127 if (type.properties) {
128 return {
129 type: 'RecordType',
130 fields: type.properties.map(propertyToField).filter(x => x)
131 };
132 }
133
134 return {
135 type: 'NameExpression',
136 name: generate(type.id, {
137 compact: true
138 }).code
139 };
140 case 'BooleanLiteralTypeAnnotation':
141 return {
142 type: 'BooleanLiteralType',
143 value: type.value
144 };
145 case 'NumberLiteralTypeAnnotation':
146 return {
147 type: 'NumericLiteralType',
148 value: type.value
149 };
150 case 'StringLiteralTypeAnnotation':
151 return {
152 type: 'StringLiteralType',
153 value: type.value
154 };
155 case 'ThisTypeAnnotation':
156 return {
157 type: 'NameExpression',
158 name: 'this'
159 };
160 default:
161 return {
162 type: 'AllLiteral'
163 };
164 }
165}
166
167module.exports = flowDoctrine;