@runtime-type-inspector/transpiler 2.2.2 → 2.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.cjs +1068 -35
- package/index.mjs +1063 -33
- package/package.json +3 -3
- package/types.d.ts +0 -970
package/index.mjs
CHANGED
|
@@ -1,11 +1,47 @@
|
|
|
1
1
|
import { parse } from '@babel/parser';
|
|
2
2
|
import ts from 'typescript';
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* @todo expandTypeDepFree doesn't support "complicated" types.
|
|
6
|
+
* For actual builds, we use expandType() anyway (which is based on TypeScript).
|
|
7
|
+
* But since TypeScript is a huge dependency, I'm looking into BabelFlow/BabelTypescript parser.
|
|
8
|
+
* Comparing AST's like this usually helps to find bugs or potential issues,
|
|
9
|
+
* while we can benchmark for best performance too.
|
|
10
|
+
* @example
|
|
11
|
+
* const tooComplex = 'Array<string|{chunks?: undefined|Array<{language: string|null, timestamp: Array<number|null>, text: string}>}>';
|
|
12
|
+
* console.log(expandTypeDepFree(tooComplex));
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* @typedef {object} ExpandTypeReturnValue
|
|
16
|
+
* @property {'array' | 'union' | 'record' | 'tuple' | 'object' | 'promise' | 'typeof'} type - The type.
|
|
17
|
+
* @property {object | string} [elementType] - For Array.
|
|
18
|
+
* @property {object | string} [key] - For Record<key, val>
|
|
19
|
+
* @property {object | string} [val] - For Record<key, val>
|
|
20
|
+
* @property {(object | string)[]} [members] - For unions.
|
|
21
|
+
* @property {object | string} [properties] - For objects.
|
|
22
|
+
* @property {(object | string)[]} [elements] - For tuples.
|
|
23
|
+
* @property {object | string} [argument] - For typeof.
|
|
24
|
+
*/
|
|
25
|
+
/**
|
|
26
|
+
* 'DepFree' refers to the fact that this function has no dependencies,
|
|
27
|
+
* while `expandType` depends on TypeScript itself for maximum compatibility.
|
|
28
|
+
* @example
|
|
29
|
+
* expandTypeDepFree('(123) '); // Outputs: '123'
|
|
30
|
+
* expandTypeDepFree('Array<number> '); // Outputs: { type: 'array', elementType: 'number' }
|
|
31
|
+
* expandTypeDepFree('Array<(123) > '); // Outputs: { type: 'array', elementType: '123' }
|
|
32
|
+
* expandTypeDepFree(' ( ( 123 ) ) '); // Outputs: '123'
|
|
33
|
+
* expandTypeDepFree(' (string ) |(number ) '); // Outputs: { type: 'union', members: ['string', 'number'] }
|
|
34
|
+
* expandTypeDepFree(' (( Object ) ) '); // Outputs: { type: 'object', properties: {} }
|
|
35
|
+
* @param {string} type - The input type to expand.
|
|
36
|
+
* @returns {string | ExpandTypeReturnValue} Object containing parsed information from type string.
|
|
37
|
+
*/
|
|
4
38
|
function expandTypeDepFree(type) {
|
|
5
39
|
type = type.trim();
|
|
40
|
+
// '(123)' -> '123'
|
|
6
41
|
while (!type.includes('|') && type[0] === '(' && type[type.length - 1] === ')') {
|
|
7
42
|
type = type.slice(1, -1).trim();
|
|
8
43
|
}
|
|
44
|
+
// (1) Rest parameters like ...string
|
|
9
45
|
if (type[0] === '.' && type[1] === '.' && type[2] === '.') {
|
|
10
46
|
const elementType = type.slice(3);
|
|
11
47
|
return {
|
|
@@ -13,6 +49,8 @@ function expandTypeDepFree(type) {
|
|
|
13
49
|
elementType
|
|
14
50
|
};
|
|
15
51
|
}
|
|
52
|
+
// (2)
|
|
53
|
+
// Array<...>
|
|
16
54
|
if (type.startsWith("Array<") && type.endsWith('>')) {
|
|
17
55
|
const typeSlice = type.slice(6, -1);
|
|
18
56
|
const elementType = expandTypeDepFree(typeSlice);
|
|
@@ -21,6 +59,7 @@ function expandTypeDepFree(type) {
|
|
|
21
59
|
elementType
|
|
22
60
|
};
|
|
23
61
|
}
|
|
62
|
+
// Promise<...>
|
|
24
63
|
if (type.startsWith("Promise<") && type.endsWith('>')) {
|
|
25
64
|
const typeSlice = type.slice(8, -1);
|
|
26
65
|
const elementType = expandTypeDepFree(typeSlice);
|
|
@@ -29,6 +68,7 @@ function expandTypeDepFree(type) {
|
|
|
29
68
|
elementType
|
|
30
69
|
};
|
|
31
70
|
}
|
|
71
|
+
// (3) Object<...> or Record<...>
|
|
32
72
|
if ((type.startsWith("Object<") || type.startsWith("Record<")) && type.endsWith('>')) {
|
|
33
73
|
const recordSlice = type.slice(7, -1);
|
|
34
74
|
const firstComma = recordSlice.indexOf(',');
|
|
@@ -43,8 +83,9 @@ function expandTypeDepFree(type) {
|
|
|
43
83
|
val: expandTypeDepFree(val)
|
|
44
84
|
};
|
|
45
85
|
}
|
|
86
|
+
// (4) {...}
|
|
46
87
|
if (type[0] === '{' && type[type.length - 1] === '}') {
|
|
47
|
-
const propertiesArray = type.slice(1, -1).split(',');
|
|
88
|
+
const propertiesArray = type.slice(1, -1).split(','); // ['entity: Entity', ' app: AppBase']
|
|
48
89
|
const properties = {};
|
|
49
90
|
propertiesArray.forEach(_ => {
|
|
50
91
|
const [propName, propType] = _.split(":").map(_ => _.trim());
|
|
@@ -59,6 +100,7 @@ function expandTypeDepFree(type) {
|
|
|
59
100
|
properties
|
|
60
101
|
};
|
|
61
102
|
}
|
|
103
|
+
// (5) expand unions
|
|
62
104
|
const members = type.split("|");
|
|
63
105
|
if (members.length >= 2) {
|
|
64
106
|
members.forEach((_, i) => members[i] = _.trim());
|
|
@@ -67,6 +109,8 @@ function expandTypeDepFree(type) {
|
|
|
67
109
|
members: members.map(expandTypeDepFree)
|
|
68
110
|
};
|
|
69
111
|
}
|
|
112
|
+
// (6) expand [] Arrays
|
|
113
|
+
// Test arrays: new pc.Mat3().set([1, 2, 3, "asd"])
|
|
70
114
|
if (type.endsWith("[]")) {
|
|
71
115
|
const typeSlice = type.slice(0, -2);
|
|
72
116
|
return {
|
|
@@ -74,13 +118,15 @@ function expandTypeDepFree(type) {
|
|
|
74
118
|
elementType: expandTypeDepFree(typeSlice)
|
|
75
119
|
};
|
|
76
120
|
}
|
|
121
|
+
// (7) expand tuples
|
|
77
122
|
if (type[0] === '[' && type[type.length - 1] === ']') {
|
|
78
|
-
const elements = type.slice(1, -1).split(',');
|
|
123
|
+
const elements = type.slice(1, -1).split(','); // ['null', ' Texture', ' Texture', ' Texture', ' Texture', ' Texture', ' Texture']
|
|
79
124
|
return {
|
|
80
125
|
type: 'tuple',
|
|
81
126
|
elements: elements.map(expandTypeDepFree)
|
|
82
127
|
};
|
|
83
128
|
}
|
|
129
|
+
// (8) expand typeof expressions
|
|
84
130
|
if (type.startsWith('typeof ')) {
|
|
85
131
|
const argument = expandTypeDepFree(type.substring(7));
|
|
86
132
|
return {
|
|
@@ -97,6 +143,14 @@ function expandTypeDepFree(type) {
|
|
|
97
143
|
return type;
|
|
98
144
|
}
|
|
99
145
|
|
|
146
|
+
/** @typedef {import('@babel/types').Node} Node */
|
|
147
|
+
/** @typedef {import('@babel/types').Function} Function */
|
|
148
|
+
/**
|
|
149
|
+
* Checks if the provided node is a function-like structure.
|
|
150
|
+
*
|
|
151
|
+
* @param {Node} node - The Babel AST node to be tested.
|
|
152
|
+
* @returns {node is Function} - `true` if the node is a function-like structure, otherwise `false`.
|
|
153
|
+
*/
|
|
100
154
|
function nodeIsFunction(node) {
|
|
101
155
|
switch (node.type) {
|
|
102
156
|
case 'ArrowFunctionExpression':
|
|
@@ -110,12 +164,23 @@ function nodeIsFunction(node) {
|
|
|
110
164
|
return false;
|
|
111
165
|
}
|
|
112
166
|
|
|
167
|
+
/**
|
|
168
|
+
* @typedef DocType
|
|
169
|
+
* @property {boolean} optional - Type is optional.
|
|
170
|
+
*/
|
|
171
|
+
/**
|
|
172
|
+
* @param {string | DocType} type - The type.
|
|
173
|
+
* @param {boolean} optional - Optionality
|
|
174
|
+
* @returns {string | DocType} The simplified type.
|
|
175
|
+
*/
|
|
113
176
|
function simplifyType(type, optional) {
|
|
177
|
+
// If it's already an object, just set optionality.
|
|
114
178
|
if (type instanceof Object) {
|
|
115
179
|
type.optional = optional;
|
|
116
180
|
} else if (typeof type === 'string') {
|
|
117
181
|
type = type.trim();
|
|
118
182
|
if (type !== 'object' && type !== 'object[]' && type !== 'union' && !optional) {
|
|
183
|
+
// console.log("simplify", type);
|
|
119
184
|
return type;
|
|
120
185
|
}
|
|
121
186
|
type = {
|
|
@@ -128,43 +193,65 @@ function simplifyType(type, optional) {
|
|
|
128
193
|
}
|
|
129
194
|
if (type.type === 'object' && type.properties && Object.keys(type.properties).length === 0) {
|
|
130
195
|
delete type.properties;
|
|
196
|
+
// console.log("delete empty", type);
|
|
131
197
|
}
|
|
198
|
+
|
|
132
199
|
return type;
|
|
133
200
|
}
|
|
134
201
|
|
|
202
|
+
/**
|
|
203
|
+
* Parses JSDoc comments to extract parameter type information.
|
|
204
|
+
*
|
|
205
|
+
* @param {string} src - The JSDoc comment string to parse.
|
|
206
|
+
* @param {Function} [expandType] - An optional function to process the types found in the JSDoc.
|
|
207
|
+
* @returns {Record<string, any> | undefined} An object mapping parameter names to their parsed types, or undefined if no parameters are found.
|
|
208
|
+
*/
|
|
135
209
|
function parseJSDoc(src, expandType = expandTypeDepFree) {
|
|
210
|
+
// Parse something like: @param {Object} [kwargs={}] Optional arguments.
|
|
136
211
|
const regex = /@param \{(.*?)\} ([\[\]a-zA-Z0-9_$=\{\}\.'" ]+)/g;
|
|
137
212
|
const matches = [...src.matchAll(regex)];
|
|
213
|
+
/** @type {Record<string, any>} */
|
|
138
214
|
const params = Object.create(null);
|
|
139
215
|
matches.forEach(_ => {
|
|
140
216
|
const type = expandType(_[1].trim());
|
|
141
217
|
let name = _[2].trim();
|
|
142
218
|
let optional = false;
|
|
219
|
+
// Examples:
|
|
220
|
+
// name: [kwargs={}] The configuration parameters.
|
|
221
|
+
// name: [d = 1.0] Sample spacing
|
|
143
222
|
if (name[0] === '[') {
|
|
223
|
+
// Possible improvement: counting opening/closing brackets for perfect match
|
|
144
224
|
const closer = name.lastIndexOf(']');
|
|
225
|
+
// Afterwards name will be: d = 1.0
|
|
145
226
|
name = name.substring(1, closer);
|
|
227
|
+
// mark it for the type:
|
|
146
228
|
optional = true;
|
|
147
229
|
}
|
|
230
|
+
// Strip the rest (either leftover of optional value or description)
|
|
148
231
|
name = name.split(' ')[0].split('=')[0].trim();
|
|
149
232
|
const simplifiedType = simplifyType(type, optional);
|
|
150
233
|
const parts = name.split(".");
|
|
151
234
|
if (parts.length === 3) {
|
|
152
|
-
|
|
153
|
-
const
|
|
154
|
-
const
|
|
235
|
+
// Something like: @param {number[]} settings.render.skyboxRotation - Rotation of skybox.
|
|
236
|
+
const parts0 = parts[0]; // settings
|
|
237
|
+
const parts1 = parts[1]; // render
|
|
238
|
+
const parts2 = parts[2]; // skyboxRotation
|
|
155
239
|
const toptype = params[parts0];
|
|
156
240
|
toptype.properties[parts1].properties = toptype.properties[parts1].properties || {};
|
|
157
241
|
toptype.properties[parts1].properties[parts2] = simplifiedType;
|
|
158
242
|
} else if (parts.length === 2) {
|
|
159
|
-
|
|
160
|
-
|
|
243
|
+
// Something like: @param {number} description[].components
|
|
244
|
+
let parts0 = parts[0]; // description[]
|
|
245
|
+
const parts1 = parts[1]; // components
|
|
161
246
|
if (parts0.endsWith('[]')) {
|
|
162
|
-
parts0 = parts0.slice(0, -2);
|
|
247
|
+
parts0 = parts0.slice(0, -2); // description[] -> description
|
|
163
248
|
}
|
|
249
|
+
|
|
164
250
|
const toptype = params[parts0];
|
|
165
251
|
if ((toptype == null ? void 0 : toptype.type) === "union") {
|
|
166
252
|
const typeObject = toptype.members.find(_ => (_ == null ? void 0 : _.type) === 'object');
|
|
167
253
|
typeObject.properties[parts1] = simplifiedType;
|
|
254
|
+
//typeObject.properties = simplifiedType; // todo add test case
|
|
168
255
|
} else if ((toptype == null ? void 0 : toptype.type) === "array") {
|
|
169
256
|
toptype.elementType.properties[parts1] = simplifiedType;
|
|
170
257
|
} else if ((toptype == null ? void 0 : toptype.type) === "object") {
|
|
@@ -189,29 +276,61 @@ function parseJSDoc(src, expandType = expandTypeDepFree) {
|
|
|
189
276
|
return params;
|
|
190
277
|
}
|
|
191
278
|
|
|
279
|
+
/**
|
|
280
|
+
* @param {string} src - JSDoc comment of the setter.
|
|
281
|
+
* @param {Function} expandType - The expandType function.
|
|
282
|
+
* @returns {string | DocType | undefined} The parsed and possibly expanded type from
|
|
283
|
+
* the JSDoc comment, or undefined if parsing fails to find `@type`.
|
|
284
|
+
*/
|
|
192
285
|
function parseJSDocSetter(src, expandType = expandTypeDepFree) {
|
|
193
286
|
const regex = /@type \{(.*?)\}/g;
|
|
194
287
|
const matches = [...src.matchAll(regex)];
|
|
195
288
|
if (matches.length === 1) {
|
|
196
289
|
const match = matches[0];
|
|
197
290
|
const type = expandType(match[1]);
|
|
198
|
-
const simplifiedType = simplifyType(type, false);
|
|
291
|
+
const simplifiedType = simplifyType(type, /* optional */false);
|
|
199
292
|
return simplifiedType;
|
|
200
293
|
}
|
|
201
294
|
}
|
|
202
295
|
|
|
296
|
+
/**
|
|
297
|
+
* Extracts the parameter name and its optionality from a JSDoc parameter string.
|
|
298
|
+
*
|
|
299
|
+
* This function takes a rest parameter string from a JSDoc comment, trims it, and determines the parameter's
|
|
300
|
+
* name and whether it is optional. The optionality is inferred based on the presence of square brackets around
|
|
301
|
+
* the parameter name.
|
|
302
|
+
*
|
|
303
|
+
* @param {string} rest - The rest part of a JSDoc parameter string to parse.
|
|
304
|
+
* @returns {[string, boolean]} A tuple where the first element is the name of the parameter,
|
|
305
|
+
* and the second element is a boolean indicating if the parameter is optional.
|
|
306
|
+
*/
|
|
203
307
|
function extractNameAndOptionality(rest) {
|
|
204
308
|
rest = rest.trim();
|
|
205
309
|
let optional = false;
|
|
310
|
+
// Examples:
|
|
311
|
+
// name: [kwargs={}] The configuration parameters.
|
|
312
|
+
// name: [d = 1.0] Sample spacing
|
|
206
313
|
if (rest[0] === '[') {
|
|
314
|
+
// Possible improvement: counting opening/closing brackets for perfect match
|
|
207
315
|
const closer = rest.lastIndexOf(']');
|
|
316
|
+
// Afterwards name will be: d = 1.0
|
|
208
317
|
rest = rest.substring(1, closer);
|
|
318
|
+
// mark it for the type:
|
|
209
319
|
optional = true;
|
|
210
320
|
}
|
|
321
|
+
// Strip the rest (either leftover of optional value or description)
|
|
211
322
|
const name = rest.split(' ')[0].split('=')[0].trim();
|
|
212
323
|
return [name, optional];
|
|
213
324
|
}
|
|
214
325
|
|
|
326
|
+
/**
|
|
327
|
+
* Extracts the content of a string that is delimited by curly braces.
|
|
328
|
+
* @example
|
|
329
|
+
* extractCurlyContent('{ {inner} }'); // Returns: {content: ' {inner} ', nextIndex: 11}
|
|
330
|
+
* @param {string} line - The string to extract from.
|
|
331
|
+
* @returns {{content: string, nextIndex: number}} An object containing the extracted content,
|
|
332
|
+
* and the index of the character immediately following the closing curly brace.
|
|
333
|
+
*/
|
|
215
334
|
function extractCurlyContent(line) {
|
|
216
335
|
const firstCurly = line.indexOf('{');
|
|
217
336
|
let k = firstCurly + 1;
|
|
@@ -233,6 +352,17 @@ function extractCurlyContent(line) {
|
|
|
233
352
|
nextIndex: k + 1
|
|
234
353
|
};
|
|
235
354
|
}
|
|
355
|
+
/**
|
|
356
|
+
* Parses JSDoc comments to extract and expand typedefs and their associated properties.
|
|
357
|
+
*
|
|
358
|
+
* It iterates through the lines of a `CommentBlock` from the Babel AST, looking for `@typedef` and `@property`
|
|
359
|
+
* annotations. When it finds a typedef, it stores it in the `typedefs` record. When it finds a property,
|
|
360
|
+
* it adds it to the last found typedef if it is an object type.
|
|
361
|
+
* @param {Record<string, object>} typedefs - An object to store typedefs, mapping type names to their expanded definitions.
|
|
362
|
+
* @param {Console["warn"]} warn - A warn function used for emitting warnings about non-extensible types.
|
|
363
|
+
* @param {import("@babel/types").Comment} comment - A comment extracted from Babel's AST, expected to be a CommentBlock containing type definitions.
|
|
364
|
+
* @param {Function} expandType - A function that takes a type expression as a string and returns a structured representation of the type.
|
|
365
|
+
*/
|
|
236
366
|
function parseJSDocTypedef(typedefs, warn, comment, expandType) {
|
|
237
367
|
const {
|
|
238
368
|
type,
|
|
@@ -254,13 +384,16 @@ function parseJSDocTypedef(typedefs, warn, comment, expandType) {
|
|
|
254
384
|
nextIndex
|
|
255
385
|
} = extractCurlyContent(line);
|
|
256
386
|
let name = line.substring(nextIndex).trim();
|
|
387
|
+
// Drop description
|
|
257
388
|
name = name.split(' ')[0];
|
|
258
389
|
lastTypedef = expandType(def);
|
|
390
|
+
// Ignore @typedef's that only refer to themselves in another file (see typedef-overwrite test)
|
|
259
391
|
if (lastTypedef !== name) {
|
|
260
392
|
typedefs[name] = lastTypedef;
|
|
261
393
|
}
|
|
262
394
|
} else if (line.startsWith('@property')) {
|
|
263
395
|
var _lastTypedef;
|
|
396
|
+
// class @property
|
|
264
397
|
if (!lastTypedef) {
|
|
265
398
|
continue;
|
|
266
399
|
}
|
|
@@ -271,6 +404,7 @@ function parseJSDocTypedef(typedefs, warn, comment, expandType) {
|
|
|
271
404
|
const rest = line.substring(nextIndex);
|
|
272
405
|
const propType = expandType(content);
|
|
273
406
|
const [name, optional] = extractNameAndOptionality(rest);
|
|
407
|
+
// console.log({name, optional, propType});
|
|
274
408
|
const finalType = simplifyType(propType, optional);
|
|
275
409
|
if (((_lastTypedef = lastTypedef) == null ? void 0 : _lastTypedef.type) === 'object') {
|
|
276
410
|
lastTypedef.properties[name] = finalType;
|
|
@@ -284,30 +418,62 @@ function parseJSDocTypedef(typedefs, warn, comment, expandType) {
|
|
|
284
418
|
}
|
|
285
419
|
}
|
|
286
420
|
|
|
421
|
+
/**
|
|
422
|
+
* @typedef Stat
|
|
423
|
+
* @property {number} checked - How often assertions were added to this kind of node.
|
|
424
|
+
* @property {number} unchecked - How often no assertions were added to this kind of node.
|
|
425
|
+
*/
|
|
426
|
+
/**
|
|
427
|
+
* @param {Stat} stat - the Stat.
|
|
428
|
+
*/
|
|
287
429
|
function statReset(stat) {
|
|
288
430
|
stat.checked = 0;
|
|
289
431
|
stat.unchecked = 0;
|
|
290
432
|
}
|
|
291
433
|
|
|
434
|
+
/**
|
|
435
|
+
* @example
|
|
436
|
+
* trimEndSpaces('test \n '); // Returns: 'test \n'
|
|
437
|
+
* @param {string} str - The input string.
|
|
438
|
+
* @returns {string} Output string without spaces at the end.
|
|
439
|
+
*/
|
|
292
440
|
function trimEndSpaces(str) {
|
|
293
441
|
let i = str.length - 1;
|
|
442
|
+
// decrement index while character is a space
|
|
294
443
|
while (str[i] === ' ') {
|
|
295
444
|
i--;
|
|
296
445
|
}
|
|
446
|
+
// return string from 0 to non-space character
|
|
297
447
|
return str.slice(0, i + 1);
|
|
298
448
|
}
|
|
299
449
|
|
|
450
|
+
/**
|
|
451
|
+
* @typedef {import("@babel/types").Node} Node
|
|
452
|
+
*/
|
|
453
|
+
/**
|
|
454
|
+
* Class for converting AST into string.
|
|
455
|
+
*/
|
|
300
456
|
class Stringifier {
|
|
301
457
|
constructor() {
|
|
302
458
|
this.forceCurly = false;
|
|
459
|
+
/** @type {Node[]} */
|
|
303
460
|
this.parents = [];
|
|
304
461
|
this.lastCommentBlockIndex = -1;
|
|
305
462
|
this.lastCommentLineIndex = -1;
|
|
306
463
|
this.numSpaces = 0;
|
|
307
464
|
}
|
|
465
|
+
/**
|
|
466
|
+
* @param {Node} node - The Babel AST node.
|
|
467
|
+
* @returns {string} Stringification of the node.
|
|
468
|
+
*/
|
|
308
469
|
toSource(node) {
|
|
309
470
|
var _node$extra;
|
|
471
|
+
// handle this case only temporarily
|
|
310
472
|
if (node === null) {
|
|
473
|
+
// Contexts like:
|
|
474
|
+
// Object.assign(Channel3d.prototype, {
|
|
475
|
+
// setPosition: function /*null*/(position) {
|
|
476
|
+
//return '/*null*/';
|
|
311
477
|
return '';
|
|
312
478
|
}
|
|
313
479
|
const {
|
|
@@ -321,6 +487,7 @@ class Stringifier {
|
|
|
321
487
|
let out = '';
|
|
322
488
|
let comments = '';
|
|
323
489
|
if (leadingComments) {
|
|
490
|
+
// comments += this.mapToSource(leadingComments).join('');
|
|
324
491
|
comments = this.leadingCommentsToSource(leadingComments);
|
|
325
492
|
}
|
|
326
493
|
if (node != null && (_node$extra = node.extra) != null && _node$extra.parenthesized) {
|
|
@@ -331,9 +498,17 @@ class Stringifier {
|
|
|
331
498
|
if (trailingComments) {
|
|
332
499
|
out += this.trailingCommentsToSource(trailingComments);
|
|
333
500
|
}
|
|
501
|
+
// leadingComments and trailingComments lead to duplicates, not always tho
|
|
502
|
+
//if (node && node.trailingComments) {
|
|
503
|
+
// out += this.mapToSource(node.trailingComments).join('\n') + '\n';
|
|
504
|
+
//}
|
|
334
505
|
this.parents.pop();
|
|
335
506
|
return out;
|
|
336
507
|
}
|
|
508
|
+
/**
|
|
509
|
+
* @param {Node} node - The Babel AST node.
|
|
510
|
+
* @returns {string} Stringification of the node.
|
|
511
|
+
*/
|
|
337
512
|
toSource_(node) {
|
|
338
513
|
if (!node) {
|
|
339
514
|
return '';
|
|
@@ -356,12 +531,18 @@ class Stringifier {
|
|
|
356
531
|
return `rtiUnhandled("${node.type}");\n`;
|
|
357
532
|
}
|
|
358
533
|
parentProvidesSpaces(reverseIndex) {
|
|
534
|
+
// May look like: .../ArrayExpression/StringLiteral/CommentLine
|
|
359
535
|
const {
|
|
360
536
|
parents
|
|
361
537
|
} = this;
|
|
362
538
|
const parent = parents[parents.length - reverseIndex];
|
|
539
|
+
// console.log("Got parent", parent.type);
|
|
363
540
|
return (parent == null ? void 0 : parent.type) === 'ArrayExpression';
|
|
364
541
|
}
|
|
542
|
+
/**
|
|
543
|
+
* @param {import("@babel/types").CommentBlock} node - The Babel AST node.
|
|
544
|
+
* @returns {string} Stringification of the node.
|
|
545
|
+
*/
|
|
365
546
|
CommentBlock(node) {
|
|
366
547
|
const {
|
|
367
548
|
loc
|
|
@@ -370,6 +551,7 @@ class Stringifier {
|
|
|
370
551
|
value
|
|
371
552
|
} = node;
|
|
372
553
|
if (this.lastCommentBlockIndex === loc.start.index) {
|
|
554
|
+
// console.log("CommentBlock> ignore double");
|
|
373
555
|
return '';
|
|
374
556
|
}
|
|
375
557
|
this.lastCommentBlockIndex = loc.start.index;
|
|
@@ -377,6 +559,7 @@ class Stringifier {
|
|
|
377
559
|
spaces
|
|
378
560
|
} = this;
|
|
379
561
|
let out = '';
|
|
562
|
+
/** @todo add option for number-of-spaces */
|
|
380
563
|
const dedicatedLine = spaces.length === loc.start.column;
|
|
381
564
|
if (dedicatedLine) {
|
|
382
565
|
out += spaces;
|
|
@@ -384,6 +567,7 @@ class Stringifier {
|
|
|
384
567
|
const multiLine = loc.start.line !== loc.end.line;
|
|
385
568
|
if (multiLine) {
|
|
386
569
|
var _this$parents$at;
|
|
570
|
+
// console.log(this.parents.at(-2)?.type === 'AssignmentExpression');
|
|
387
571
|
if (((_this$parents$at = this.parents.at(-2)) == null ? void 0 : _this$parents$at.type) === 'AssignmentExpression') {
|
|
388
572
|
spaces += ' ';
|
|
389
573
|
}
|
|
@@ -391,7 +575,11 @@ class Stringifier {
|
|
|
391
575
|
}
|
|
392
576
|
out += '/*';
|
|
393
577
|
if (value.includes('\n')) {
|
|
394
|
-
|
|
578
|
+
// A bit tricky to handle multiline comments,
|
|
579
|
+
// we have to remove the given indentation level.
|
|
580
|
+
value = trimEndSpaces(value.replace(/^\s*\*/gm, '*')).split('\n').filter(_ => _.length) // skip empty lines
|
|
581
|
+
// Skip spaces in first line for /**
|
|
582
|
+
.map((line, i) => (i ? spaces + ' ' : '') + line).join('\n');
|
|
395
583
|
out += `${value}\n${spaces} */`;
|
|
396
584
|
} else {
|
|
397
585
|
out += `${value}*/`;
|
|
@@ -403,12 +591,17 @@ class Stringifier {
|
|
|
403
591
|
}
|
|
404
592
|
return out;
|
|
405
593
|
}
|
|
594
|
+
/**
|
|
595
|
+
* @param {import("@babel/types").CommentLine} node - The Babel AST node.
|
|
596
|
+
* @returns {string} Stringification of the node.
|
|
597
|
+
*/
|
|
406
598
|
CommentLine(node) {
|
|
407
599
|
const {
|
|
408
600
|
value,
|
|
409
601
|
loc
|
|
410
602
|
} = node;
|
|
411
603
|
if (this.lastCommentLineIndex === loc.start.index) {
|
|
604
|
+
// console.log("CommentLine> ignore double");
|
|
412
605
|
return '';
|
|
413
606
|
}
|
|
414
607
|
this.lastCommentLineIndex = loc.start.index;
|
|
@@ -430,6 +623,8 @@ class Stringifier {
|
|
|
430
623
|
}
|
|
431
624
|
leadingCommentsToSource(leadingComments) {
|
|
432
625
|
let out = leadingComments.map(_ => this.commentToSource(_, 'leading')).join('');
|
|
626
|
+
// The comments "took" the spaces, which e.g. ArrayExpression added,
|
|
627
|
+
// so we have to add them back now for parents that provide spaces.
|
|
433
628
|
if (this.parentProvidesSpaces(2)) {
|
|
434
629
|
out += this.spaces;
|
|
435
630
|
}
|
|
@@ -443,6 +638,7 @@ class Stringifier {
|
|
|
443
638
|
loc
|
|
444
639
|
} = trailingComment;
|
|
445
640
|
const dedicatedLine = this.spaces.length === loc.start.column;
|
|
641
|
+
// out += ` /*dedicatedLine=${dedicatedLine}*/ `;
|
|
446
642
|
if (dedicatedLine) {
|
|
447
643
|
out += '\n';
|
|
448
644
|
} else {
|
|
@@ -453,8 +649,14 @@ class Stringifier {
|
|
|
453
649
|
}
|
|
454
650
|
return out;
|
|
455
651
|
}
|
|
652
|
+
/**
|
|
653
|
+
* @param {*} comment - The comment "node" (not a real Babel Node node though, hence this special treatment)
|
|
654
|
+
* @param {'leading'|'trailing'} pos - The position, either 'leading' or 'trailing'.
|
|
655
|
+
* @returns {string} The comment "node" as a string.
|
|
656
|
+
*/
|
|
456
657
|
commentToSource(comment, pos) {
|
|
457
658
|
if (comment.type === 'CommentBlock') {
|
|
659
|
+
//if (pos === 'leading') {}
|
|
458
660
|
return this.CommentBlock(comment);
|
|
459
661
|
} else if (comment.type === 'CommentLine') {
|
|
460
662
|
return this.CommentLine(comment);
|
|
@@ -463,6 +665,12 @@ class Stringifier {
|
|
|
463
665
|
debugger;
|
|
464
666
|
return '';
|
|
465
667
|
}
|
|
668
|
+
/**
|
|
669
|
+
* Only add { and } when requested (e.g. for Asserter).
|
|
670
|
+
* showAST("if (true) 2;") vs showAST("if (true) {2}")
|
|
671
|
+
* @param {Node} node - The Babel AST node.
|
|
672
|
+
* @returns {string} Stringification of the node.
|
|
673
|
+
*/
|
|
466
674
|
toSourceCurly(node) {
|
|
467
675
|
if (!this.forceCurly) {
|
|
468
676
|
return this.toSource(node);
|
|
@@ -475,7 +683,10 @@ class Stringifier {
|
|
|
475
683
|
let out = '';
|
|
476
684
|
if (needCurly) {
|
|
477
685
|
out += ' {\n';
|
|
686
|
+
//out += spaces + `/* toSourceCurly> needCurly=${needCurly} type=${type}
|
|
687
|
+
//parents=${parents.map(_=>_.type).join('->')}*/\n`;
|
|
478
688
|
}
|
|
689
|
+
|
|
479
690
|
out += this.toSource(node);
|
|
480
691
|
if (needCurly) {
|
|
481
692
|
out += '\n';
|
|
@@ -495,12 +706,29 @@ class Stringifier {
|
|
|
495
706
|
const t = this.parentType;
|
|
496
707
|
return t !== "ForStatement" && t !== "ForInStatement" && t !== "ForOfStatement";
|
|
497
708
|
}
|
|
709
|
+
/**
|
|
710
|
+
* @param {Node[]} arr - Array of nodes to convert.
|
|
711
|
+
* @returns {string[]} Array of strings for each node.
|
|
712
|
+
*/
|
|
498
713
|
mapToSource(arr) {
|
|
499
714
|
return arr.map(_ => this.toSource(_));
|
|
500
715
|
}
|
|
716
|
+
/**
|
|
717
|
+
* Generates a string representing type checks for a given Babel AST node.
|
|
718
|
+
*
|
|
719
|
+
* Note: This method serves as a stub and should be overridden in subclasses.
|
|
720
|
+
* The actual implementation is expected to be provided in Asserter.mjs, where
|
|
721
|
+
* it would create runtime type assertions based on the AST node provided.
|
|
722
|
+
*
|
|
723
|
+
* @param {Node} node - The Babel AST node for which to generate type checks.
|
|
724
|
+
* @returns {string} A placeholder string, as this stub implementation does nothing; expected to be overridden.
|
|
725
|
+
*/
|
|
501
726
|
generateTypeChecks(node) {
|
|
502
727
|
return '';
|
|
503
728
|
}
|
|
729
|
+
/**
|
|
730
|
+
* @type {string} A string of two spaces per indentation.
|
|
731
|
+
*/
|
|
504
732
|
get spaces() {
|
|
505
733
|
return ' '.repeat(this.numSpaces);
|
|
506
734
|
}
|
|
@@ -513,18 +741,32 @@ class Stringifier {
|
|
|
513
741
|
const path = parents.map(_ => _.type).join('/');
|
|
514
742
|
return `${spaces}// got ${n} spaces for ${path}\n`;
|
|
515
743
|
}
|
|
744
|
+
/**
|
|
745
|
+
* > await something();
|
|
746
|
+
*
|
|
747
|
+
* @param {import("@babel/types").AwaitExpression} node - The Babel AST node.
|
|
748
|
+
* @returns {string} Stringification of the node.
|
|
749
|
+
*/
|
|
516
750
|
AwaitExpression(node) {
|
|
517
751
|
const {
|
|
518
752
|
argument
|
|
519
753
|
} = node;
|
|
520
754
|
return `await ${this.toSource(argument)}`;
|
|
521
755
|
}
|
|
756
|
+
/**
|
|
757
|
+
* @param {import("@babel/types").ClassBody} node - The Babel AST node.
|
|
758
|
+
* @returns {string} Stringification of the node.
|
|
759
|
+
*/
|
|
522
760
|
ClassBody(node) {
|
|
523
761
|
const {
|
|
524
762
|
body
|
|
525
763
|
} = node;
|
|
526
764
|
return this.mapToSource(body).join('\n');
|
|
527
765
|
}
|
|
766
|
+
/**
|
|
767
|
+
* @param {import("@babel/types").ClassMethod} node - The Babel AST node.
|
|
768
|
+
* @returns {string} Stringification of the node.
|
|
769
|
+
*/
|
|
528
770
|
ClassMethod(node) {
|
|
529
771
|
const {
|
|
530
772
|
static: static_,
|
|
@@ -566,6 +808,10 @@ class Stringifier {
|
|
|
566
808
|
out += this.toSource(body);
|
|
567
809
|
return out;
|
|
568
810
|
}
|
|
811
|
+
/**
|
|
812
|
+
* @param {import("@babel/types").ClassExpression} node - The Babel AST node.
|
|
813
|
+
* @returns {string} Stringification of the node.
|
|
814
|
+
*/
|
|
569
815
|
ClassExpression(node) {
|
|
570
816
|
const {
|
|
571
817
|
id,
|
|
@@ -574,6 +820,7 @@ class Stringifier {
|
|
|
574
820
|
} = node;
|
|
575
821
|
let out = 'class ';
|
|
576
822
|
if (id !== null) {
|
|
823
|
+
// Babel: strange API, either null or undefined... pick a type, maybe oversight/bug
|
|
577
824
|
out += this.toSource(id) + ' ';
|
|
578
825
|
}
|
|
579
826
|
if (superClass !== null) {
|
|
@@ -584,6 +831,10 @@ class Stringifier {
|
|
|
584
831
|
out += `{\n${c}\n}`;
|
|
585
832
|
return out;
|
|
586
833
|
}
|
|
834
|
+
/**
|
|
835
|
+
* @param {import("@babel/types").ClassDeclaration} node - The Babel AST node.
|
|
836
|
+
* @returns {string} Stringification of the node.
|
|
837
|
+
*/
|
|
587
838
|
ClassDeclaration(node) {
|
|
588
839
|
const {
|
|
589
840
|
id,
|
|
@@ -605,6 +856,10 @@ class Stringifier {
|
|
|
605
856
|
out += '\n}\n';
|
|
606
857
|
return out;
|
|
607
858
|
}
|
|
859
|
+
/**
|
|
860
|
+
* @param {import("@babel/types").ClassPrivateMethod} node - The Babel AST node.
|
|
861
|
+
* @returns {string} Stringification of the node.
|
|
862
|
+
*/
|
|
608
863
|
ClassPrivateMethod(node) {
|
|
609
864
|
const {
|
|
610
865
|
static: static_,
|
|
@@ -641,6 +896,15 @@ class Stringifier {
|
|
|
641
896
|
out += this.toSource(body);
|
|
642
897
|
return out;
|
|
643
898
|
}
|
|
899
|
+
/**
|
|
900
|
+
* > asd;
|
|
901
|
+
* > asd = 1;
|
|
902
|
+
* > static asd;
|
|
903
|
+
* > static asd = 1;
|
|
904
|
+
*
|
|
905
|
+
* @param {import("@babel/types").ClassProperty} node - The Babel AST node.
|
|
906
|
+
* @returns {string} Stringification of the node.
|
|
907
|
+
*/
|
|
644
908
|
ClassProperty(node) {
|
|
645
909
|
const {
|
|
646
910
|
key,
|
|
@@ -648,7 +912,7 @@ class Stringifier {
|
|
|
648
912
|
value,
|
|
649
913
|
leadingComments
|
|
650
914
|
} = node;
|
|
651
|
-
const static_ = node.static;
|
|
915
|
+
const static_ = node.static; // JS keyword is problematic in object destructuring
|
|
652
916
|
const a = this.toSource(key);
|
|
653
917
|
const b = this.toSource(value);
|
|
654
918
|
let out = this.spaces;
|
|
@@ -666,6 +930,10 @@ class Stringifier {
|
|
|
666
930
|
out += ';';
|
|
667
931
|
return out;
|
|
668
932
|
}
|
|
933
|
+
/**
|
|
934
|
+
* @param {import("@babel/types").ClassPrivateProperty} node - The Babel AST node.
|
|
935
|
+
* @returns {string} Stringification of the node.
|
|
936
|
+
*/
|
|
669
937
|
ClassPrivateProperty(node) {
|
|
670
938
|
const {
|
|
671
939
|
static: static_,
|
|
@@ -683,6 +951,10 @@ class Stringifier {
|
|
|
683
951
|
out += ';';
|
|
684
952
|
return out;
|
|
685
953
|
}
|
|
954
|
+
/**
|
|
955
|
+
* @param {import("@babel/types").ContinueStatement} node - The Babel AST node.
|
|
956
|
+
* @returns {string} Stringification of the node.
|
|
957
|
+
*/
|
|
686
958
|
ContinueStatement(node) {
|
|
687
959
|
const {
|
|
688
960
|
label
|
|
@@ -694,9 +966,22 @@ class Stringifier {
|
|
|
694
966
|
out += ';';
|
|
695
967
|
return out;
|
|
696
968
|
}
|
|
969
|
+
/**
|
|
970
|
+
* Converts an array of Babel AST nodes representing function parameters into a comma-separated string.
|
|
971
|
+
*
|
|
972
|
+
* Each parameter node is converted to its source representation and combined into a single
|
|
973
|
+
* string suitable for inserting into a function declaration's parentheses.
|
|
974
|
+
*
|
|
975
|
+
* @param {Node[]} params - An array of Babel AST nodes representing the function parameters to be stringified.
|
|
976
|
+
* @returns {string} A string representing the serialized parameters, enclosed in parentheses.
|
|
977
|
+
*/
|
|
697
978
|
FunctionDeclarationParams(params) {
|
|
698
979
|
return '(' + this.mapToSource(params).join(', ') + ')';
|
|
699
980
|
}
|
|
981
|
+
/**
|
|
982
|
+
* @param {import("@babel/types").FunctionDeclaration} node - The Babel AST node.
|
|
983
|
+
* @returns {string} Stringification of the node.
|
|
984
|
+
*/
|
|
700
985
|
FunctionDeclaration(node) {
|
|
701
986
|
const {
|
|
702
987
|
async,
|
|
@@ -718,13 +1003,18 @@ class Stringifier {
|
|
|
718
1003
|
out += this.toSource(body);
|
|
719
1004
|
return out;
|
|
720
1005
|
}
|
|
1006
|
+
/**
|
|
1007
|
+
* @param {import("@babel/types").FunctionExpression} node - The Babel AST node.
|
|
1008
|
+
* @returns {string} Stringification of the node.
|
|
1009
|
+
*/
|
|
721
1010
|
FunctionExpression(node) {
|
|
1011
|
+
// Leading comments for asd=function(){} are in ExpressionStatement
|
|
722
1012
|
const {
|
|
723
1013
|
async,
|
|
724
1014
|
body,
|
|
725
1015
|
generator,
|
|
726
|
-
id
|
|
727
|
-
params
|
|
1016
|
+
id /*, leadingComments*/,
|
|
1017
|
+
params /*, extra*/
|
|
728
1018
|
} = node;
|
|
729
1019
|
let out = '';
|
|
730
1020
|
if (node.leadingComments) {
|
|
@@ -744,12 +1034,16 @@ class Stringifier {
|
|
|
744
1034
|
}
|
|
745
1035
|
return out;
|
|
746
1036
|
}
|
|
1037
|
+
/**
|
|
1038
|
+
* @param {import("@babel/types").ArrowFunctionExpression} node - The Babel AST node.
|
|
1039
|
+
* @returns {string} Stringification of the node.
|
|
1040
|
+
*/
|
|
747
1041
|
ArrowFunctionExpression(node) {
|
|
748
1042
|
const {
|
|
749
1043
|
async,
|
|
750
1044
|
body,
|
|
751
1045
|
generator,
|
|
752
|
-
params
|
|
1046
|
+
params /*, extra*/
|
|
753
1047
|
} = node;
|
|
754
1048
|
let out = '';
|
|
755
1049
|
if (async) {
|
|
@@ -763,6 +1057,10 @@ class Stringifier {
|
|
|
763
1057
|
out += this.toSource(body);
|
|
764
1058
|
return out;
|
|
765
1059
|
}
|
|
1060
|
+
/**
|
|
1061
|
+
* @param {import("@babel/types").BigIntLiteral} node - The Babel AST node.
|
|
1062
|
+
* @returns {string} Stringification of the node.
|
|
1063
|
+
*/
|
|
766
1064
|
BigIntLiteral(node) {
|
|
767
1065
|
const {
|
|
768
1066
|
extra,
|
|
@@ -770,6 +1068,10 @@ class Stringifier {
|
|
|
770
1068
|
} = node;
|
|
771
1069
|
return extra.raw;
|
|
772
1070
|
}
|
|
1071
|
+
/**
|
|
1072
|
+
* @param {import("@babel/types").BlockStatement} node - The Babel AST node.
|
|
1073
|
+
* @returns {string} Stringification of the node.
|
|
1074
|
+
*/
|
|
773
1075
|
BlockStatement(node) {
|
|
774
1076
|
const {
|
|
775
1077
|
body,
|
|
@@ -779,6 +1081,7 @@ class Stringifier {
|
|
|
779
1081
|
let out = '';
|
|
780
1082
|
out += ' {';
|
|
781
1083
|
this.numSpaces++;
|
|
1084
|
+
// Handle Directive/DirectiveLiteral like 'use strict';
|
|
782
1085
|
if (directives && directives.length) {
|
|
783
1086
|
out += '\n';
|
|
784
1087
|
out += this.mapToSource(directives).join('\n') + '\n';
|
|
@@ -786,8 +1089,10 @@ class Stringifier {
|
|
|
786
1089
|
out += this.generateTypeChecks(node);
|
|
787
1090
|
if (body.length) {
|
|
788
1091
|
if (out.length === 2) {
|
|
1092
|
+
// 2 is ' {'
|
|
789
1093
|
out += '\n';
|
|
790
1094
|
}
|
|
1095
|
+
// out += '/*'+out.length+'*/';
|
|
791
1096
|
out += this.mapToSource(body).join('\n') + '\n';
|
|
792
1097
|
out += spaces;
|
|
793
1098
|
}
|
|
@@ -795,12 +1100,24 @@ class Stringifier {
|
|
|
795
1100
|
out += '}';
|
|
796
1101
|
return out;
|
|
797
1102
|
}
|
|
1103
|
+
/**
|
|
1104
|
+
* > 'use strict';
|
|
1105
|
+
*
|
|
1106
|
+
* @param {import("@babel/types").Directive} node - The Babel AST node.
|
|
1107
|
+
* @returns {string} Stringification of the node.
|
|
1108
|
+
*/
|
|
798
1109
|
Directive(node) {
|
|
799
1110
|
const {
|
|
800
1111
|
value
|
|
801
1112
|
} = node;
|
|
802
1113
|
return this.toSource(value);
|
|
803
1114
|
}
|
|
1115
|
+
/**
|
|
1116
|
+
* > 'use strict';
|
|
1117
|
+
*
|
|
1118
|
+
* @param {import("@babel/types").DirectiveLiteral} node - The Babel AST node.
|
|
1119
|
+
* @returns {string} Stringification of the node.
|
|
1120
|
+
*/
|
|
804
1121
|
DirectiveLiteral(node) {
|
|
805
1122
|
const {
|
|
806
1123
|
extra,
|
|
@@ -811,6 +1128,10 @@ class Stringifier {
|
|
|
811
1128
|
} = this;
|
|
812
1129
|
return `${spaces}${extra.raw};`;
|
|
813
1130
|
}
|
|
1131
|
+
/**
|
|
1132
|
+
* @param {import("@babel/types").ReturnStatement} node - The Babel AST node.
|
|
1133
|
+
* @returns {string} Stringification of the node.
|
|
1134
|
+
*/
|
|
814
1135
|
ReturnStatement(node) {
|
|
815
1136
|
const {
|
|
816
1137
|
argument
|
|
@@ -823,9 +1144,17 @@ class Stringifier {
|
|
|
823
1144
|
}
|
|
824
1145
|
return spaces + 'return ' + this.toSource(argument) + ';';
|
|
825
1146
|
}
|
|
1147
|
+
/**
|
|
1148
|
+
* @param {import("@babel/types").Identifier} node - The Babel AST node.
|
|
1149
|
+
* @returns {string} Stringification of the node.
|
|
1150
|
+
*/
|
|
826
1151
|
Identifier(node) {
|
|
827
1152
|
return node.name;
|
|
828
1153
|
}
|
|
1154
|
+
/**
|
|
1155
|
+
* @param {import("@babel/types").IfStatement} node - The Babel AST node.
|
|
1156
|
+
* @returns {string} Stringification of the node.
|
|
1157
|
+
*/
|
|
829
1158
|
IfStatement(node) {
|
|
830
1159
|
const {
|
|
831
1160
|
alternate,
|
|
@@ -834,7 +1163,10 @@ class Stringifier {
|
|
|
834
1163
|
} = node;
|
|
835
1164
|
const spaces = this.spaces;
|
|
836
1165
|
let out = '';
|
|
1166
|
+
//if (this.parentType !== "IfStatement")
|
|
1167
|
+
//{
|
|
837
1168
|
out += spaces;
|
|
1169
|
+
//}
|
|
838
1170
|
out += `if (${this.toSource(test)})`;
|
|
839
1171
|
out += this.toSourceCurly(consequent);
|
|
840
1172
|
if (alternate) {
|
|
@@ -842,6 +1174,10 @@ class Stringifier {
|
|
|
842
1174
|
}
|
|
843
1175
|
return out;
|
|
844
1176
|
}
|
|
1177
|
+
/**
|
|
1178
|
+
* @param {import("@babel/types").LabeledStatement} node - The Babel AST node.
|
|
1179
|
+
* @returns {string} Stringification of the node.
|
|
1180
|
+
*/
|
|
845
1181
|
LabeledStatement(node) {
|
|
846
1182
|
const {
|
|
847
1183
|
body,
|
|
@@ -856,6 +1192,15 @@ class Stringifier {
|
|
|
856
1192
|
out += spaces + this.toSource(body);
|
|
857
1193
|
return out;
|
|
858
1194
|
}
|
|
1195
|
+
/**
|
|
1196
|
+
* @example
|
|
1197
|
+
* ts = require("typescript");
|
|
1198
|
+
* ts.createSourceFile("repl.ts", "a = 1", ts.ScriptTarget.Latest);
|
|
1199
|
+
* ts.createSourceFile("repl.ts", "!!(a = 1 + 2)", ts.ScriptTarget.Latest);
|
|
1200
|
+
* showAST('!!(a = 1)');
|
|
1201
|
+
* @param {import("@babel/types").UnaryExpression} node - The Babel AST node.
|
|
1202
|
+
* @returns {string} Stringification of the node.
|
|
1203
|
+
*/
|
|
859
1204
|
UnaryExpression(node) {
|
|
860
1205
|
let out = '';
|
|
861
1206
|
const {
|
|
@@ -866,6 +1211,7 @@ class Stringifier {
|
|
|
866
1211
|
if (!prefix) {
|
|
867
1212
|
console.warn("Stringifier#UnaryExpression> never considered !prefix", node);
|
|
868
1213
|
}
|
|
1214
|
+
// Add a space for: typeof/delete/void
|
|
869
1215
|
const c = operator.charCodeAt(0);
|
|
870
1216
|
let op = operator;
|
|
871
1217
|
if (c >= 97 && c <= 122) {
|
|
@@ -875,6 +1221,10 @@ class Stringifier {
|
|
|
875
1221
|
out += this.toSource(argument);
|
|
876
1222
|
return out;
|
|
877
1223
|
}
|
|
1224
|
+
/**
|
|
1225
|
+
* @param {import("@babel/types").MemberExpression} node - The Babel AST node.
|
|
1226
|
+
* @returns {string} Stringification of the node.
|
|
1227
|
+
*/
|
|
878
1228
|
MemberExpression(node) {
|
|
879
1229
|
const {
|
|
880
1230
|
computed,
|
|
@@ -886,6 +1236,10 @@ class Stringifier {
|
|
|
886
1236
|
}
|
|
887
1237
|
return `${this.toSource(object)}.${this.toSource(property)}`;
|
|
888
1238
|
}
|
|
1239
|
+
/**
|
|
1240
|
+
* @param {import("@babel/types").MetaProperty} node - The Babel AST node.
|
|
1241
|
+
* @returns {string} Stringification of the node.
|
|
1242
|
+
*/
|
|
889
1243
|
MetaProperty(node) {
|
|
890
1244
|
const {
|
|
891
1245
|
meta,
|
|
@@ -895,12 +1249,26 @@ class Stringifier {
|
|
|
895
1249
|
const rhs = this.toSource(property);
|
|
896
1250
|
return `${lhs}.${rhs}`;
|
|
897
1251
|
}
|
|
1252
|
+
/**
|
|
1253
|
+
* @param {import("@babel/types").ExpressionStatement} node - The Babel AST node.
|
|
1254
|
+
* @returns {string} Stringification of the node.
|
|
1255
|
+
*/
|
|
898
1256
|
ExpressionStatement(node) {
|
|
899
1257
|
const {
|
|
900
1258
|
expression
|
|
901
1259
|
} = node;
|
|
1260
|
+
/**
|
|
1261
|
+
* Not just a fall-through, adds a semicolon when it has semantic meaning.
|
|
1262
|
+
* E.g. invalid:
|
|
1263
|
+
* (function() {})()
|
|
1264
|
+
* (function() {})()
|
|
1265
|
+
*/
|
|
902
1266
|
return this.spaces + this.toSource(expression) + ';';
|
|
903
1267
|
}
|
|
1268
|
+
/**
|
|
1269
|
+
* @param {import("@babel/types").CallExpression} node - The Babel AST node.
|
|
1270
|
+
* @returns {string} Stringification of the node.
|
|
1271
|
+
*/
|
|
904
1272
|
CallExpression(node) {
|
|
905
1273
|
const {
|
|
906
1274
|
callee,
|
|
@@ -916,6 +1284,14 @@ class Stringifier {
|
|
|
916
1284
|
out += ")";
|
|
917
1285
|
return out;
|
|
918
1286
|
}
|
|
1287
|
+
/**
|
|
1288
|
+
* We replicate the exact AST for validation:
|
|
1289
|
+
* x = 1;
|
|
1290
|
+
* y = {x,}
|
|
1291
|
+
* z = {y};
|
|
1292
|
+
* @param {import("@babel/types").ObjectExpression} node - The Babel AST node.
|
|
1293
|
+
* @returns {string} Stringification of the node.
|
|
1294
|
+
*/
|
|
919
1295
|
ObjectExpression(node) {
|
|
920
1296
|
const {
|
|
921
1297
|
properties,
|
|
@@ -937,6 +1313,10 @@ class Stringifier {
|
|
|
937
1313
|
out += '}';
|
|
938
1314
|
return out;
|
|
939
1315
|
}
|
|
1316
|
+
/**
|
|
1317
|
+
* @param {import("@babel/types").ObjectProperty} node - The Babel AST node.
|
|
1318
|
+
* @returns {string} Stringification of the node.
|
|
1319
|
+
*/
|
|
940
1320
|
ObjectProperty(node) {
|
|
941
1321
|
const {
|
|
942
1322
|
computed,
|
|
@@ -964,9 +1344,17 @@ class Stringifier {
|
|
|
964
1344
|
out += right;
|
|
965
1345
|
return out;
|
|
966
1346
|
}
|
|
1347
|
+
/**
|
|
1348
|
+
* @param {import("@babel/types").BooleanLiteral} node - The Babel AST node.
|
|
1349
|
+
* @returns {string} Stringification of the node.
|
|
1350
|
+
*/
|
|
967
1351
|
BooleanLiteral(node) {
|
|
968
1352
|
return node.value.toString();
|
|
969
1353
|
}
|
|
1354
|
+
/**
|
|
1355
|
+
* @param {import("@babel/types").AssignmentExpression} node - The Babel AST node.
|
|
1356
|
+
* @returns {string} Stringification of the node.
|
|
1357
|
+
*/
|
|
970
1358
|
AssignmentExpression(node) {
|
|
971
1359
|
const {
|
|
972
1360
|
left,
|
|
@@ -975,8 +1363,13 @@ class Stringifier {
|
|
|
975
1363
|
} = node;
|
|
976
1364
|
const left_ = this.toSource(left);
|
|
977
1365
|
const right_ = this.toSource(right);
|
|
1366
|
+
// operator is for example: = |= &=
|
|
978
1367
|
return `${left_} ${operator} ${right_}`;
|
|
979
1368
|
}
|
|
1369
|
+
/**
|
|
1370
|
+
* @param {import("@babel/types").BinaryExpression} node - The Babel AST node.
|
|
1371
|
+
* @returns {string} Stringification of the node.
|
|
1372
|
+
*/
|
|
980
1373
|
BinaryExpression(node) {
|
|
981
1374
|
const {
|
|
982
1375
|
left,
|
|
@@ -987,9 +1380,17 @@ class Stringifier {
|
|
|
987
1380
|
const right_ = this.toSource(right);
|
|
988
1381
|
return `${left_} ${operator} ${right_}`;
|
|
989
1382
|
}
|
|
1383
|
+
/**
|
|
1384
|
+
* @param {import("@babel/types").ThisExpression} node - The Babel AST node.
|
|
1385
|
+
* @returns {string} Stringification of the node.
|
|
1386
|
+
*/
|
|
990
1387
|
ThisExpression(node) {
|
|
991
1388
|
return 'this';
|
|
992
1389
|
}
|
|
1390
|
+
/**
|
|
1391
|
+
* @param {import("@babel/types").ArrayExpression} node - The Babel AST node.
|
|
1392
|
+
* @returns {string} Stringification of the node.
|
|
1393
|
+
*/
|
|
993
1394
|
ArrayExpression(node) {
|
|
994
1395
|
const {
|
|
995
1396
|
elements,
|
|
@@ -1012,6 +1413,10 @@ class Stringifier {
|
|
|
1012
1413
|
out += '\n' + this.spaces + ']';
|
|
1013
1414
|
return out;
|
|
1014
1415
|
}
|
|
1416
|
+
/**
|
|
1417
|
+
* @param {import("@babel/types").VariableDeclaration} node - The Babel AST node.
|
|
1418
|
+
* @returns {string} Stringification of the node.
|
|
1419
|
+
*/
|
|
1015
1420
|
VariableDeclaration(node) {
|
|
1016
1421
|
const {
|
|
1017
1422
|
declarations,
|
|
@@ -1025,6 +1430,10 @@ class Stringifier {
|
|
|
1025
1430
|
}
|
|
1026
1431
|
return `${spaces}${kind} ${this.mapToSource(declarations).join(', ')}${semicolon}`;
|
|
1027
1432
|
}
|
|
1433
|
+
/**
|
|
1434
|
+
* @param {import("@babel/types").VariableDeclarator} node - The Babel AST node.
|
|
1435
|
+
* @returns {string} Stringification of the node.
|
|
1436
|
+
*/
|
|
1028
1437
|
VariableDeclarator(node) {
|
|
1029
1438
|
const {
|
|
1030
1439
|
id,
|
|
@@ -1035,6 +1444,10 @@ class Stringifier {
|
|
|
1035
1444
|
}
|
|
1036
1445
|
return this.toSource(id);
|
|
1037
1446
|
}
|
|
1447
|
+
/**
|
|
1448
|
+
* @param {import("@babel/types").ConditionalExpression} node - The Babel AST node.
|
|
1449
|
+
* @returns {string} Stringification of the node.
|
|
1450
|
+
*/
|
|
1038
1451
|
ConditionalExpression(node) {
|
|
1039
1452
|
const {
|
|
1040
1453
|
alternate,
|
|
@@ -1043,6 +1456,12 @@ class Stringifier {
|
|
|
1043
1456
|
} = node;
|
|
1044
1457
|
return `${this.toSource(test)} ? ${this.toSource(consequent)} : ${this.toSource(alternate)}`;
|
|
1045
1458
|
}
|
|
1459
|
+
/**
|
|
1460
|
+
* if (...)
|
|
1461
|
+
*
|
|
1462
|
+
* @param {import("@babel/types").LogicalExpression} node - The Babel AST node.
|
|
1463
|
+
* @returns {string} Stringification of the node.
|
|
1464
|
+
*/
|
|
1046
1465
|
LogicalExpression(node) {
|
|
1047
1466
|
const {
|
|
1048
1467
|
left,
|
|
@@ -1059,6 +1478,12 @@ class Stringifier {
|
|
|
1059
1478
|
}
|
|
1060
1479
|
return `${l} ${operator} ${r}`;
|
|
1061
1480
|
}
|
|
1481
|
+
/**
|
|
1482
|
+
* TODO TEST: can init be undefined in for(;;)
|
|
1483
|
+
*
|
|
1484
|
+
* @param {import("@babel/types").ForStatement} node - The Babel AST node.
|
|
1485
|
+
* @returns {string} Stringification of the node.
|
|
1486
|
+
*/
|
|
1062
1487
|
ForStatement(node) {
|
|
1063
1488
|
const {
|
|
1064
1489
|
init,
|
|
@@ -1073,6 +1498,12 @@ class Stringifier {
|
|
|
1073
1498
|
const b = this.toSourceCurly(body);
|
|
1074
1499
|
return `${spaces}for (${i}; ${t}; ${u})${b}`;
|
|
1075
1500
|
}
|
|
1501
|
+
/**
|
|
1502
|
+
* showAST("++i")
|
|
1503
|
+
*
|
|
1504
|
+
* @param {import("@babel/types").UpdateExpression} node - The Babel AST node.
|
|
1505
|
+
* @returns {string} Stringification of the node.
|
|
1506
|
+
*/
|
|
1076
1507
|
UpdateExpression(node) {
|
|
1077
1508
|
const {
|
|
1078
1509
|
operator,
|
|
@@ -1084,6 +1515,10 @@ class Stringifier {
|
|
|
1084
1515
|
}
|
|
1085
1516
|
return `${this.toSource(argument)}${operator}`;
|
|
1086
1517
|
}
|
|
1518
|
+
/**
|
|
1519
|
+
* @param {import("@babel/types").NewExpression} node - The Babel AST node.
|
|
1520
|
+
* @returns {string} Stringification of the node.
|
|
1521
|
+
*/
|
|
1087
1522
|
NewExpression(node) {
|
|
1088
1523
|
const {
|
|
1089
1524
|
callee,
|
|
@@ -1093,6 +1528,12 @@ class Stringifier {
|
|
|
1093
1528
|
const args = this.mapToSource(args_).join(', ');
|
|
1094
1529
|
return `new ${c}(${args})`;
|
|
1095
1530
|
}
|
|
1531
|
+
/**
|
|
1532
|
+
* @example
|
|
1533
|
+
* console.log(ast2json(parseSync("`${1} b ${2+3}c`").program.body[0]));
|
|
1534
|
+
* @param {import("@babel/types").TemplateLiteral} node - The Babel AST node.
|
|
1535
|
+
* @returns {string} Stringification of the node.
|
|
1536
|
+
*/
|
|
1096
1537
|
TemplateLiteral(node) {
|
|
1097
1538
|
const {
|
|
1098
1539
|
expressions,
|
|
@@ -1108,12 +1549,20 @@ class Stringifier {
|
|
|
1108
1549
|
out += '`';
|
|
1109
1550
|
return out;
|
|
1110
1551
|
}
|
|
1552
|
+
/**
|
|
1553
|
+
* @param {import("@babel/types").TemplateElement} node - The Babel AST node.
|
|
1554
|
+
* @returns {string} Stringification of the node.
|
|
1555
|
+
*/
|
|
1111
1556
|
TemplateElement(node) {
|
|
1112
1557
|
const {
|
|
1113
1558
|
value
|
|
1114
1559
|
} = node;
|
|
1115
1560
|
return value.raw;
|
|
1116
1561
|
}
|
|
1562
|
+
/**
|
|
1563
|
+
* @param {import("@babel/types").ParenthesizedExpression} node - The Babel AST node.
|
|
1564
|
+
* @returns {string} Stringification of the node.
|
|
1565
|
+
*/
|
|
1117
1566
|
ParenthesizedExpression(node) {
|
|
1118
1567
|
const {
|
|
1119
1568
|
expression,
|
|
@@ -1130,21 +1579,37 @@ class Stringifier {
|
|
|
1130
1579
|
out += '\n' + this.spaces + ')';
|
|
1131
1580
|
return out;
|
|
1132
1581
|
}
|
|
1582
|
+
/**
|
|
1583
|
+
* @param {import("@babel/types").PrivateName} node - The Babel AST node.
|
|
1584
|
+
* @returns {string} Stringification of the node.
|
|
1585
|
+
*/
|
|
1133
1586
|
PrivateName(node) {
|
|
1134
1587
|
const {
|
|
1135
1588
|
id
|
|
1136
1589
|
} = node;
|
|
1137
1590
|
return `#${this.toSource(id)}`;
|
|
1138
1591
|
}
|
|
1592
|
+
/**
|
|
1593
|
+
* @param {import("@babel/types").ExportAllDeclaration} node - The Babel AST node.
|
|
1594
|
+
* @returns {string} Stringification of the node.
|
|
1595
|
+
*/
|
|
1139
1596
|
ExportAllDeclaration(node) {
|
|
1140
1597
|
return `export * from ${node.source.extra.raw};`;
|
|
1141
1598
|
}
|
|
1599
|
+
/**
|
|
1600
|
+
* @param {import("@babel/types").ExportNamespaceSpecifier} node - The Babel AST node.
|
|
1601
|
+
* @returns {string} Stringification of the node.
|
|
1602
|
+
*/
|
|
1142
1603
|
ExportNamespaceSpecifier(node) {
|
|
1143
1604
|
const {
|
|
1144
1605
|
exported
|
|
1145
1606
|
} = node;
|
|
1146
1607
|
return '* as ' + this.toSource(exported);
|
|
1147
1608
|
}
|
|
1609
|
+
/**
|
|
1610
|
+
* @param {import("@babel/types").ExportNamedDeclaration} node - The Babel AST node.
|
|
1611
|
+
* @returns {string} Stringification of the node.
|
|
1612
|
+
*/
|
|
1148
1613
|
ExportNamedDeclaration(node) {
|
|
1149
1614
|
const {
|
|
1150
1615
|
declaration,
|
|
@@ -1171,6 +1636,7 @@ class Stringifier {
|
|
|
1171
1636
|
out += '}';
|
|
1172
1637
|
} else {
|
|
1173
1638
|
out += 'export {\n';
|
|
1639
|
+
// todo: fix spacing system
|
|
1174
1640
|
out += this.mapToSource(specifiers).map(_ => ` ${this.spaces}${_}`).join(',\n');
|
|
1175
1641
|
out += '\n';
|
|
1176
1642
|
out += this.spaces;
|
|
@@ -1188,6 +1654,10 @@ class Stringifier {
|
|
|
1188
1654
|
}
|
|
1189
1655
|
return out;
|
|
1190
1656
|
}
|
|
1657
|
+
/**
|
|
1658
|
+
* @param {import("@babel/types").ExportDefaultDeclaration} node - The Babel AST node.
|
|
1659
|
+
* @returns {string} Stringification of the node.
|
|
1660
|
+
*/
|
|
1191
1661
|
ExportDefaultDeclaration(node) {
|
|
1192
1662
|
const {
|
|
1193
1663
|
declaration
|
|
@@ -1195,6 +1665,10 @@ class Stringifier {
|
|
|
1195
1665
|
const a = this.toSource(declaration);
|
|
1196
1666
|
return `export default ${a};`;
|
|
1197
1667
|
}
|
|
1668
|
+
/**
|
|
1669
|
+
* @param {import("@babel/types").ExportSpecifier} node - The Babel AST node.
|
|
1670
|
+
* @returns {string} Stringification of the node.
|
|
1671
|
+
*/
|
|
1198
1672
|
ExportSpecifier(node) {
|
|
1199
1673
|
const {
|
|
1200
1674
|
exported,
|
|
@@ -1208,11 +1682,22 @@ class Stringifier {
|
|
|
1208
1682
|
if (left === right) {
|
|
1209
1683
|
return `${spaces}${left}`;
|
|
1210
1684
|
}
|
|
1685
|
+
// For example:
|
|
1686
|
+
// PostEffect: PostEffect$1\n
|
|
1687
|
+
// createMesh: createMesh$1\n
|
|
1211
1688
|
return `${spaces}${right} as ${left}`;
|
|
1212
1689
|
}
|
|
1690
|
+
/**
|
|
1691
|
+
* @param {import("@babel/types").Super} node - The Babel AST node.
|
|
1692
|
+
* @returns {string} Stringification of the node.
|
|
1693
|
+
*/
|
|
1213
1694
|
Super(node) {
|
|
1214
1695
|
return 'super';
|
|
1215
1696
|
}
|
|
1697
|
+
/**
|
|
1698
|
+
* @param {import("@babel/types").ForInStatement} node - The Babel AST node.
|
|
1699
|
+
* @returns {string} Stringification of the node.
|
|
1700
|
+
*/
|
|
1216
1701
|
ForInStatement(node) {
|
|
1217
1702
|
const {
|
|
1218
1703
|
left,
|
|
@@ -1227,6 +1712,10 @@ class Stringifier {
|
|
|
1227
1712
|
const b = this.toSourceCurly(body);
|
|
1228
1713
|
return `${spaces}for (${l} in ${r})${b}`;
|
|
1229
1714
|
}
|
|
1715
|
+
/**
|
|
1716
|
+
* @param {import("@babel/types").ThrowStatement} node - The Babel AST node.
|
|
1717
|
+
* @returns {string} Stringification of the node.
|
|
1718
|
+
*/
|
|
1230
1719
|
ThrowStatement(node) {
|
|
1231
1720
|
const {
|
|
1232
1721
|
argument
|
|
@@ -1237,6 +1726,10 @@ class Stringifier {
|
|
|
1237
1726
|
const arg = this.toSource(argument);
|
|
1238
1727
|
return `${spaces}throw ${arg};`;
|
|
1239
1728
|
}
|
|
1729
|
+
/**
|
|
1730
|
+
* @param {import("@babel/types").WhileStatement} node - The Babel AST node.
|
|
1731
|
+
* @returns {string} Stringification of the node.
|
|
1732
|
+
*/
|
|
1240
1733
|
WhileStatement(node) {
|
|
1241
1734
|
const {
|
|
1242
1735
|
test,
|
|
@@ -1249,6 +1742,10 @@ class Stringifier {
|
|
|
1249
1742
|
const b = this.toSourceCurly(body);
|
|
1250
1743
|
return `${spaces}while (${t})${b}`;
|
|
1251
1744
|
}
|
|
1745
|
+
/**
|
|
1746
|
+
* @param {import("@babel/types").BreakStatement} node - The Babel AST node.
|
|
1747
|
+
* @returns {string} Stringification of the node.
|
|
1748
|
+
*/
|
|
1252
1749
|
BreakStatement(node) {
|
|
1253
1750
|
const {
|
|
1254
1751
|
label
|
|
@@ -1260,6 +1757,10 @@ class Stringifier {
|
|
|
1260
1757
|
out += ';';
|
|
1261
1758
|
return out;
|
|
1262
1759
|
}
|
|
1760
|
+
/**
|
|
1761
|
+
* @param {import("@babel/types").ForOfStatement} node - The Babel AST node.
|
|
1762
|
+
* @returns {string} Stringification of the node.
|
|
1763
|
+
*/
|
|
1263
1764
|
ForOfStatement(node) {
|
|
1264
1765
|
const {
|
|
1265
1766
|
await: await_,
|
|
@@ -1276,6 +1777,13 @@ class Stringifier {
|
|
|
1276
1777
|
const a = await_ ? 'await ' : '';
|
|
1277
1778
|
return `${spaces}for ${a}(${l} of ${r})${b}`;
|
|
1278
1779
|
}
|
|
1780
|
+
/**
|
|
1781
|
+
* for (const [a, b] in c)
|
|
1782
|
+
* --> [a, b] is the array pattern
|
|
1783
|
+
*
|
|
1784
|
+
* @param {import("@babel/types").ArrayPattern} node - The Babel AST node.
|
|
1785
|
+
* @returns {string} Stringification of the node.
|
|
1786
|
+
*/
|
|
1279
1787
|
ArrayPattern(node) {
|
|
1280
1788
|
const {
|
|
1281
1789
|
elements
|
|
@@ -1283,6 +1791,10 @@ class Stringifier {
|
|
|
1283
1791
|
const e = this.mapToSource(elements).join(', ');
|
|
1284
1792
|
return `[${e}]`;
|
|
1285
1793
|
}
|
|
1794
|
+
/**
|
|
1795
|
+
* @param {import("@babel/types").SwitchStatement} node - The Babel AST node.
|
|
1796
|
+
* @returns {string} Stringification of the node.
|
|
1797
|
+
*/
|
|
1286
1798
|
SwitchStatement(node) {
|
|
1287
1799
|
const {
|
|
1288
1800
|
discriminant,
|
|
@@ -1297,6 +1809,10 @@ class Stringifier {
|
|
|
1297
1809
|
out += spaces + '}';
|
|
1298
1810
|
return out;
|
|
1299
1811
|
}
|
|
1812
|
+
/**
|
|
1813
|
+
* @param {import("@babel/types").SwitchCase} node - The Babel AST node.
|
|
1814
|
+
* @returns {string} Stringification of the node.
|
|
1815
|
+
*/
|
|
1300
1816
|
SwitchCase(node) {
|
|
1301
1817
|
const {
|
|
1302
1818
|
consequent,
|
|
@@ -1312,7 +1828,13 @@ class Stringifier {
|
|
|
1312
1828
|
}
|
|
1313
1829
|
return `${spaces}default:\n${c}`;
|
|
1314
1830
|
}
|
|
1831
|
+
/**
|
|
1832
|
+
* @param {import("@babel/types").RegExpLiteral} node - The Babel AST node.
|
|
1833
|
+
* @returns {string} Stringification of the node.
|
|
1834
|
+
*/
|
|
1315
1835
|
RegExpLiteral(node) {
|
|
1836
|
+
// parseSync("/test/gm")
|
|
1837
|
+
// todo figure out why it always exposes node.value === undefined... oversight in Babel?
|
|
1316
1838
|
const {
|
|
1317
1839
|
extra,
|
|
1318
1840
|
value,
|
|
@@ -1321,15 +1843,32 @@ class Stringifier {
|
|
|
1321
1843
|
} = node;
|
|
1322
1844
|
return extra.raw;
|
|
1323
1845
|
}
|
|
1846
|
+
/**
|
|
1847
|
+
* for (var i=0, n=arr.length; i<n; i++)
|
|
1848
|
+
* sequence expression: var i=0, n=arr.length
|
|
1849
|
+
*
|
|
1850
|
+
* @param {import("@babel/types").SequenceExpression} node - The Babel AST node.
|
|
1851
|
+
* @returns {string} Stringification of the node.
|
|
1852
|
+
*/
|
|
1324
1853
|
SequenceExpression(node) {
|
|
1325
1854
|
const {
|
|
1326
1855
|
expressions
|
|
1327
1856
|
} = node;
|
|
1328
1857
|
return this.mapToSource(expressions).join(', ');
|
|
1329
1858
|
}
|
|
1859
|
+
/**
|
|
1860
|
+
* showAST("if (true);");
|
|
1861
|
+
*
|
|
1862
|
+
* @param {import("@babel/types").EmptyStatement} node - The Babel AST node.
|
|
1863
|
+
* @returns {string} Stringification of the node.
|
|
1864
|
+
*/
|
|
1330
1865
|
EmptyStatement(node) {
|
|
1331
1866
|
return ';';
|
|
1332
1867
|
}
|
|
1868
|
+
/**
|
|
1869
|
+
* @param {import("@babel/types").SpreadElement} node - The Babel AST node.
|
|
1870
|
+
* @returns {string} Stringification of the node.
|
|
1871
|
+
*/
|
|
1333
1872
|
SpreadElement(node) {
|
|
1334
1873
|
const {
|
|
1335
1874
|
argument
|
|
@@ -1337,7 +1876,13 @@ class Stringifier {
|
|
|
1337
1876
|
const a = this.toSource(argument);
|
|
1338
1877
|
return `...${a}`;
|
|
1339
1878
|
}
|
|
1879
|
+
/**
|
|
1880
|
+
* @param {import("@babel/types").ObjectMethod} node - The Babel AST node.
|
|
1881
|
+
* @returns {string} Stringification of the node.
|
|
1882
|
+
*/
|
|
1340
1883
|
ObjectMethod(node) {
|
|
1884
|
+
// @todo PR in Babel why method/id is a key in node... (seems like a bug/oversight)
|
|
1885
|
+
// method === true just indicates kind === 'method'
|
|
1341
1886
|
const {
|
|
1342
1887
|
method,
|
|
1343
1888
|
key,
|
|
@@ -1353,6 +1898,10 @@ class Stringifier {
|
|
|
1353
1898
|
console.warn("ObjectMethod> id !== null is unhandled", node);
|
|
1354
1899
|
debugger;
|
|
1355
1900
|
}
|
|
1901
|
+
// if (method) {
|
|
1902
|
+
// console.warn("ObjectMethod> node was a method", node);
|
|
1903
|
+
// debugger;
|
|
1904
|
+
// }
|
|
1356
1905
|
let out = this.spaces;
|
|
1357
1906
|
if (generator) {
|
|
1358
1907
|
out += '*';
|
|
@@ -1379,6 +1928,11 @@ class Stringifier {
|
|
|
1379
1928
|
out += this.toSource(body);
|
|
1380
1929
|
return out;
|
|
1381
1930
|
}
|
|
1931
|
+
/**
|
|
1932
|
+
* E.g. function testComma({x,y,z,}) {}
|
|
1933
|
+
* @param {import("@babel/types").ObjectPattern} node - The Babel AST node.
|
|
1934
|
+
* @returns {string} Stringification of the node.
|
|
1935
|
+
*/
|
|
1382
1936
|
ObjectPattern(node) {
|
|
1383
1937
|
const {
|
|
1384
1938
|
properties,
|
|
@@ -1399,6 +1953,16 @@ class Stringifier {
|
|
|
1399
1953
|
out += '}';
|
|
1400
1954
|
return out;
|
|
1401
1955
|
}
|
|
1956
|
+
/**
|
|
1957
|
+
* > x?.y
|
|
1958
|
+
* > x?.y.z
|
|
1959
|
+
* > x[y?.z]
|
|
1960
|
+
* > x?.[y?.z]
|
|
1961
|
+
* > b?.file?.variants[variant]
|
|
1962
|
+
*
|
|
1963
|
+
* @param {import("@babel/types").OptionalMemberExpression} node - The Babel AST node.
|
|
1964
|
+
* @returns {string} Stringification of the node.
|
|
1965
|
+
*/
|
|
1402
1966
|
OptionalMemberExpression(node) {
|
|
1403
1967
|
const {
|
|
1404
1968
|
object,
|
|
@@ -1408,6 +1972,7 @@ class Stringifier {
|
|
|
1408
1972
|
} = node;
|
|
1409
1973
|
const a = this.toSource(object);
|
|
1410
1974
|
const b = this.toSource(property);
|
|
1975
|
+
// Basically four cases: computed=true/false optional=true/false, 00, 01, 10, 11
|
|
1411
1976
|
if (!computed && !optional) {
|
|
1412
1977
|
return `${a}.${b}`;
|
|
1413
1978
|
} else if (!computed && optional) {
|
|
@@ -1415,8 +1980,17 @@ class Stringifier {
|
|
|
1415
1980
|
} else if (computed && !optional) {
|
|
1416
1981
|
return `${a}[${b}]`;
|
|
1417
1982
|
}
|
|
1983
|
+
// Can only be case 4 now (computed && optional):
|
|
1418
1984
|
return `${a}?.[${b}]`;
|
|
1419
1985
|
}
|
|
1986
|
+
/**
|
|
1987
|
+
* > version?.indexOf('$');
|
|
1988
|
+
* > version?.indexOf?.('$');
|
|
1989
|
+
* > this.passEncoder?.pushDebugGroup(name);
|
|
1990
|
+
*
|
|
1991
|
+
* @param {import("@babel/types").OptionalCallExpression} node - The Babel AST node.
|
|
1992
|
+
* @returns {string} Stringification of the node.
|
|
1993
|
+
*/
|
|
1420
1994
|
OptionalCallExpression(node) {
|
|
1421
1995
|
const {
|
|
1422
1996
|
callee,
|
|
@@ -1430,6 +2004,10 @@ class Stringifier {
|
|
|
1430
2004
|
}
|
|
1431
2005
|
return `${a}(${b})`;
|
|
1432
2006
|
}
|
|
2007
|
+
/**
|
|
2008
|
+
* @param {import("@babel/types").DoWhileStatement} node - The Babel AST node.
|
|
2009
|
+
* @returns {string} Stringification of the node.
|
|
2010
|
+
*/
|
|
1433
2011
|
DoWhileStatement(node) {
|
|
1434
2012
|
const {
|
|
1435
2013
|
body,
|
|
@@ -1445,6 +2023,10 @@ class Stringifier {
|
|
|
1445
2023
|
out += ` while (${this.toSource(test)});`;
|
|
1446
2024
|
return out;
|
|
1447
2025
|
}
|
|
2026
|
+
/**
|
|
2027
|
+
* @param {import("@babel/types").TryStatement} node - The Babel AST node.
|
|
2028
|
+
* @returns {string} Stringification of the node.
|
|
2029
|
+
*/
|
|
1448
2030
|
TryStatement(node) {
|
|
1449
2031
|
const {
|
|
1450
2032
|
block,
|
|
@@ -1462,6 +2044,10 @@ class Stringifier {
|
|
|
1462
2044
|
}
|
|
1463
2045
|
return out;
|
|
1464
2046
|
}
|
|
2047
|
+
/**
|
|
2048
|
+
* @param {import("@babel/types").CatchClause} node - The Babel AST node.
|
|
2049
|
+
* @returns {string} Stringification of the node.
|
|
2050
|
+
*/
|
|
1465
2051
|
CatchClause(node) {
|
|
1466
2052
|
const {
|
|
1467
2053
|
param,
|
|
@@ -1472,25 +2058,45 @@ class Stringifier {
|
|
|
1472
2058
|
const p = this.toSource(param);
|
|
1473
2059
|
return ` catch (${p}) ${b}`;
|
|
1474
2060
|
}
|
|
2061
|
+
// Example: try { 1n / 0 } catch {}
|
|
1475
2062
|
return ` catch ${b}`;
|
|
1476
2063
|
}
|
|
2064
|
+
/**
|
|
2065
|
+
* @param {import("@babel/types").RestElement} node - The Babel AST node.
|
|
2066
|
+
* @returns {string} Stringification of the node.
|
|
2067
|
+
*/
|
|
1477
2068
|
RestElement(node) {
|
|
1478
2069
|
const {
|
|
1479
2070
|
argument
|
|
1480
2071
|
} = node;
|
|
1481
2072
|
return `...${this.toSource(argument)}`;
|
|
1482
2073
|
}
|
|
2074
|
+
/**
|
|
2075
|
+
* @param {import("@babel/types").DebuggerStatement} node - The Babel AST node.
|
|
2076
|
+
* @returns {string} Stringification of the node.
|
|
2077
|
+
*/
|
|
1483
2078
|
DebuggerStatement(node) {
|
|
1484
2079
|
return `${this.spaces}debugger;`;
|
|
1485
2080
|
}
|
|
2081
|
+
/**
|
|
2082
|
+
* @param {import("@babel/types").Import} node - The Babel AST node.
|
|
2083
|
+
* @returns {string} Stringification of the node.
|
|
2084
|
+
*/
|
|
1486
2085
|
Import(node) {
|
|
1487
2086
|
return 'import';
|
|
1488
2087
|
}
|
|
2088
|
+
/**
|
|
2089
|
+
* @param {import("@babel/types").ImportDeclaration} node - The Babel AST node.
|
|
2090
|
+
* @returns {string} Stringification of the node.
|
|
2091
|
+
*/
|
|
1489
2092
|
ImportDeclaration(node) {
|
|
1490
2093
|
const {
|
|
1491
2094
|
specifiers,
|
|
1492
2095
|
source
|
|
1493
2096
|
} = node;
|
|
2097
|
+
// ImportNamespaceSpecifier: import * as a from "b";
|
|
2098
|
+
// ImportDefaultSpecifier : import a from "b";
|
|
2099
|
+
// ImportSpecifier : import { a } from "b";
|
|
1494
2100
|
const a = this.mapToSource(specifiers).join(', ');
|
|
1495
2101
|
const b = this.toSource(source);
|
|
1496
2102
|
if (specifiers.length === 0) {
|
|
@@ -1501,6 +2107,10 @@ class Stringifier {
|
|
|
1501
2107
|
}
|
|
1502
2108
|
return `import {${a}} from ${b};`;
|
|
1503
2109
|
}
|
|
2110
|
+
/**
|
|
2111
|
+
* @param {import("@babel/types").ImportSpecifier} node - The Babel AST node.
|
|
2112
|
+
* @returns {string} Stringification of the node.
|
|
2113
|
+
*/
|
|
1504
2114
|
ImportSpecifier(node) {
|
|
1505
2115
|
const {
|
|
1506
2116
|
imported,
|
|
@@ -1513,35 +2123,59 @@ class Stringifier {
|
|
|
1513
2123
|
}
|
|
1514
2124
|
return a;
|
|
1515
2125
|
}
|
|
2126
|
+
/**
|
|
2127
|
+
* @param {import("@babel/types").ImportDefaultSpecifier} node - The Babel AST node.
|
|
2128
|
+
* @returns {string} Stringification of the node.
|
|
2129
|
+
*/
|
|
1516
2130
|
ImportDefaultSpecifier(node) {
|
|
1517
2131
|
const {
|
|
1518
2132
|
local
|
|
1519
2133
|
} = node;
|
|
1520
2134
|
return this.toSource(local);
|
|
1521
2135
|
}
|
|
2136
|
+
/**
|
|
2137
|
+
* @param {import("@babel/types").ImportNamespaceSpecifier} node - The Babel AST node.
|
|
2138
|
+
* @returns {string} Stringification of the node.
|
|
2139
|
+
*/
|
|
1522
2140
|
ImportNamespaceSpecifier(node) {
|
|
1523
2141
|
const {
|
|
1524
2142
|
local
|
|
1525
2143
|
} = node;
|
|
1526
2144
|
return `* as ${this.toSource(local)}`;
|
|
1527
2145
|
}
|
|
2146
|
+
/**
|
|
2147
|
+
* @param {import("@babel/types").File} node - The Babel AST node.
|
|
2148
|
+
* @returns {string} Stringification of the node.
|
|
2149
|
+
*/
|
|
1528
2150
|
File(node) {
|
|
1529
2151
|
return this.toSource(node.program) + '\n';
|
|
1530
2152
|
}
|
|
2153
|
+
/**
|
|
2154
|
+
* @param {import("@babel/types").Program} node - The Babel AST node.
|
|
2155
|
+
* @returns {string} Stringification of the node.
|
|
2156
|
+
*/
|
|
1531
2157
|
Program(node) {
|
|
1532
2158
|
const {
|
|
1533
|
-
body,
|
|
2159
|
+
/*sourceType, interpreter,*/body,
|
|
1534
2160
|
directives
|
|
1535
2161
|
} = node;
|
|
1536
2162
|
let out = '';
|
|
2163
|
+
// @todo I would like to keep comments above and below,
|
|
2164
|
+
// but below one is currently dropped (does't matter for AST)
|
|
2165
|
+
// See: test/typechecking/directive.mjs
|
|
1537
2166
|
out += this.mapToSource(directives);
|
|
1538
2167
|
out += this.mapToSource(body).join('\n');
|
|
1539
2168
|
return out;
|
|
1540
2169
|
}
|
|
2170
|
+
/**
|
|
2171
|
+
* @param {import("@babel/types").StringLiteral} node - The Babel AST node.
|
|
2172
|
+
* @returns {string} Stringification of the node.
|
|
2173
|
+
*/
|
|
1541
2174
|
StringLiteral(node) {
|
|
1542
2175
|
const {
|
|
1543
2176
|
extra
|
|
1544
2177
|
} = node;
|
|
2178
|
+
// Never experienced this so far, but types are types...
|
|
1545
2179
|
if (!extra) {
|
|
1546
2180
|
debugger;
|
|
1547
2181
|
return '';
|
|
@@ -1552,9 +2186,17 @@ class Stringifier {
|
|
|
1552
2186
|
}
|
|
1553
2187
|
return extra.raw;
|
|
1554
2188
|
}
|
|
2189
|
+
/**
|
|
2190
|
+
* @param {import("@babel/types").NumericLiteral} node - The Babel AST node.
|
|
2191
|
+
* @returns {string} Stringification of the node.
|
|
2192
|
+
*/
|
|
1555
2193
|
NumericLiteral(node) {
|
|
1556
2194
|
return node.extra.raw;
|
|
1557
2195
|
}
|
|
2196
|
+
/**
|
|
2197
|
+
* @param {import("@babel/types").AssignmentPattern} node - The Babel AST node.
|
|
2198
|
+
* @returns {string} Stringification of the node.
|
|
2199
|
+
*/
|
|
1558
2200
|
AssignmentPattern(node) {
|
|
1559
2201
|
const {
|
|
1560
2202
|
left,
|
|
@@ -1562,9 +2204,17 @@ class Stringifier {
|
|
|
1562
2204
|
} = node;
|
|
1563
2205
|
return `${this.toSource(left)} = ${this.toSource(right)}`;
|
|
1564
2206
|
}
|
|
2207
|
+
/**
|
|
2208
|
+
* @param {import("@babel/types").NullLiteral} node - The Babel AST node.
|
|
2209
|
+
* @returns {string} Stringification of the node.
|
|
2210
|
+
*/
|
|
1565
2211
|
NullLiteral(node) {
|
|
1566
2212
|
return 'null';
|
|
1567
2213
|
}
|
|
2214
|
+
/**
|
|
2215
|
+
* @param {import("@babel/types").TaggedTemplateExpression} node - The Babel AST node.
|
|
2216
|
+
* @returns {string} Stringification of the node.
|
|
2217
|
+
*/
|
|
1568
2218
|
TaggedTemplateExpression(node) {
|
|
1569
2219
|
const {
|
|
1570
2220
|
tag,
|
|
@@ -1574,6 +2224,10 @@ class Stringifier {
|
|
|
1574
2224
|
const q = this.toSource(quasi);
|
|
1575
2225
|
return t + q;
|
|
1576
2226
|
}
|
|
2227
|
+
/**
|
|
2228
|
+
* @param {import("@babel/types").YieldExpression} node - The Babel AST node.
|
|
2229
|
+
* @returns {string} Stringification of the node.
|
|
2230
|
+
*/
|
|
1577
2231
|
YieldExpression(node) {
|
|
1578
2232
|
const {
|
|
1579
2233
|
delegate,
|
|
@@ -1588,15 +2242,33 @@ class Stringifier {
|
|
|
1588
2242
|
}
|
|
1589
2243
|
}
|
|
1590
2244
|
|
|
2245
|
+
/** @typedef {import('@babel/types').Node } Node */
|
|
2246
|
+
/** @typedef {import("@babel/types").ClassMethod } ClassMethod */
|
|
2247
|
+
/** @typedef {import("@babel/types").ClassPrivateMethod} ClassPrivateMethod */
|
|
2248
|
+
/** @typedef {import('./stat.mjs').Stat } Stat */
|
|
2249
|
+
/**
|
|
2250
|
+
* @typedef {object} Options
|
|
2251
|
+
* @property {boolean} [forceCurly] - Determines whether curly braces are enforced in Stringifier.
|
|
2252
|
+
* @property {boolean} [validateDivision] - Indicates whether division operations should be validated.
|
|
2253
|
+
* @property {Function} [expandType] - A function that expands shorthand types into full descriptions.
|
|
2254
|
+
* @property {string} [filename] - The name of a file to which the instance pertains.
|
|
2255
|
+
* @property {boolean} [addHeader] - Whether to add import declarations headers. Defaults to true.
|
|
2256
|
+
* @property {string[]} [ignoreLocations] - Ignore these locations because they are known false-positives.
|
|
2257
|
+
*/
|
|
1591
2258
|
class Asserter extends Stringifier {
|
|
2259
|
+
/**
|
|
2260
|
+
* @param {Options} [options] - The options.
|
|
2261
|
+
*/
|
|
1592
2262
|
constructor({
|
|
1593
2263
|
forceCurly = true,
|
|
1594
2264
|
validateDivision = true,
|
|
1595
2265
|
expandType = expandTypeDepFree,
|
|
1596
2266
|
filename,
|
|
1597
|
-
addHeader = true
|
|
2267
|
+
addHeader = true,
|
|
2268
|
+
ignoreLocations = []
|
|
1598
2269
|
} = {}) {
|
|
1599
2270
|
super();
|
|
2271
|
+
/** @type {Record<string, Stat>} */
|
|
1600
2272
|
this.stats = {
|
|
1601
2273
|
'ArrowFunctionExpression': {
|
|
1602
2274
|
checked: 0,
|
|
@@ -1643,19 +2315,29 @@ class Asserter extends Stringifier {
|
|
|
1643
2315
|
unchecked: 0
|
|
1644
2316
|
}
|
|
1645
2317
|
};
|
|
2318
|
+
/** @type {Record<string, object>} */
|
|
1646
2319
|
this.typedefs = {};
|
|
1647
2320
|
this.forceCurly = forceCurly;
|
|
1648
2321
|
this.validateDivision = validateDivision;
|
|
2322
|
+
// @todo collect every type + manually validate as test set
|
|
2323
|
+
// + implement expandType using Babel Flow type parser aswell
|
|
1649
2324
|
this.expandType = expandType;
|
|
1650
2325
|
this.filename = filename;
|
|
1651
2326
|
this.addHeader = addHeader;
|
|
1652
|
-
|
|
2327
|
+
this.ignoreLocations = ignoreLocations;
|
|
2328
|
+
}
|
|
2329
|
+
/**
|
|
2330
|
+
* We expand type-asserted ArrowFunctionExpressions in order to add type assertions.
|
|
2331
|
+
* @override
|
|
2332
|
+
* @param {import("@babel/types").ArrowFunctionExpression} node - The Babel AST node.
|
|
2333
|
+
* @returns {string} Stringification of the node.
|
|
2334
|
+
*/
|
|
1653
2335
|
ArrowFunctionExpression(node) {
|
|
1654
2336
|
const {
|
|
1655
2337
|
async,
|
|
1656
2338
|
body,
|
|
1657
2339
|
generator,
|
|
1658
|
-
params
|
|
2340
|
+
params /*, extra*/
|
|
1659
2341
|
} = node;
|
|
1660
2342
|
let out = '';
|
|
1661
2343
|
if (async) {
|
|
@@ -1676,6 +2358,11 @@ class Asserter extends Stringifier {
|
|
|
1676
2358
|
}
|
|
1677
2359
|
return out;
|
|
1678
2360
|
}
|
|
2361
|
+
/**
|
|
2362
|
+
* @override
|
|
2363
|
+
* @param {import("@babel/types").ClassDeclaration} node - The Babel AST node.
|
|
2364
|
+
* @returns {string} Stringification of the node.
|
|
2365
|
+
*/
|
|
1679
2366
|
ClassDeclaration(node) {
|
|
1680
2367
|
const {
|
|
1681
2368
|
id
|
|
@@ -1685,6 +2372,14 @@ class Asserter extends Stringifier {
|
|
|
1685
2372
|
out += `${this.spaces}registerClass(${id_});`;
|
|
1686
2373
|
return out;
|
|
1687
2374
|
}
|
|
2375
|
+
/**
|
|
2376
|
+
* Finds the closest ancestor of the given node that matches the specified type.
|
|
2377
|
+
*
|
|
2378
|
+
* @param {Node} node - The starting node to search from.
|
|
2379
|
+
* @param {T} type - Type name of the node to search for.
|
|
2380
|
+
* @template {Node['type']} T
|
|
2381
|
+
* @returns {Extract<Node, {type: T}>|undefined} The first ancestor node of the specified type, or undefined if none is found.
|
|
2382
|
+
*/
|
|
1688
2383
|
findParentOfType(node, type) {
|
|
1689
2384
|
const currentIndex = this.parents.findLastIndex(_ => _ === node);
|
|
1690
2385
|
return this.parents.findLast((_, i) => {
|
|
@@ -1694,6 +2389,13 @@ class Asserter extends Stringifier {
|
|
|
1694
2389
|
return _.type === type;
|
|
1695
2390
|
});
|
|
1696
2391
|
}
|
|
2392
|
+
/**
|
|
2393
|
+
* Alternatively "import * as rti from ..." would also prevent "Unused external imports" warning...
|
|
2394
|
+
* or keeping log of every single call during RTI parsing.
|
|
2395
|
+
* @todo
|
|
2396
|
+
* Once we went over every node, we can see if we really require registerTypef, registerClass etc.
|
|
2397
|
+
* @returns {string} The import declaration header for importing RTI.
|
|
2398
|
+
*/
|
|
1697
2399
|
getHeader() {
|
|
1698
2400
|
if (!this.addHeader) {
|
|
1699
2401
|
return '';
|
|
@@ -1703,9 +2405,19 @@ class Asserter extends Stringifier {
|
|
|
1703
2405
|
header += ", validateDivision";
|
|
1704
2406
|
}
|
|
1705
2407
|
header += ", registerTypedef, registerClass} from '@runtime-type-inspector/runtime';\n";
|
|
2408
|
+
// Prevent tree-shaking in UMD build so we can always "add a breakpoint here".
|
|
1706
2409
|
header += "export * from '@runtime-type-inspector/runtime';\n";
|
|
1707
2410
|
return header;
|
|
1708
2411
|
}
|
|
2412
|
+
/**
|
|
2413
|
+
* Retrieves the node associated with the leading comments for an arrow function expression.
|
|
2414
|
+
*
|
|
2415
|
+
* This method travels up the syntax tree from the given node to find a parent node with leading comments.
|
|
2416
|
+
* This search is bounded by function boundaries or a 'CallExpression' node, as per the logic defined within the loop.
|
|
2417
|
+
* @param {Node} node - The node representing the arrow function expression for which to find the leading comments node.
|
|
2418
|
+
* @returns {Node|undefined} The node that contains the leading comments, or `undefined`
|
|
2419
|
+
* if none is found before reaching a different function or 'CallExpression'.
|
|
2420
|
+
*/
|
|
1709
2421
|
getLeadingCommentsNodeForArrowFunctionExpression(node) {
|
|
1710
2422
|
const {
|
|
1711
2423
|
parents
|
|
@@ -1715,9 +2427,16 @@ class Asserter extends Stringifier {
|
|
|
1715
2427
|
if (parent.leadingComments) {
|
|
1716
2428
|
return parent;
|
|
1717
2429
|
}
|
|
2430
|
+
// Skip now, if we find another function first,
|
|
2431
|
+
// there is no JSDoc for our function anymore.
|
|
2432
|
+
// Not interested in our start node if it didn't
|
|
2433
|
+
// contain leadingComments.
|
|
1718
2434
|
i--;
|
|
1719
2435
|
while (i >= 0) {
|
|
1720
2436
|
parent = parents[i];
|
|
2437
|
+
//if (parent.type === 'CallExpression') {
|
|
2438
|
+
// break;
|
|
2439
|
+
//}
|
|
1721
2440
|
if (nodeIsFunction(parent)) {
|
|
1722
2441
|
break;
|
|
1723
2442
|
}
|
|
@@ -1727,6 +2446,10 @@ class Asserter extends Stringifier {
|
|
|
1727
2446
|
i--;
|
|
1728
2447
|
}
|
|
1729
2448
|
}
|
|
2449
|
+
/**
|
|
2450
|
+
* Emits a warning message to the console, optionally prefixed with the instance's filename.
|
|
2451
|
+
* @param {...any} args - A list of arguments to be passed to the console.warn function.
|
|
2452
|
+
*/
|
|
1730
2453
|
warn(...args) {
|
|
1731
2454
|
if (this.filename) {
|
|
1732
2455
|
console.warn("[WARN]", this.filename);
|
|
@@ -1742,9 +2465,16 @@ class Asserter extends Stringifier {
|
|
|
1742
2465
|
if (parent.leadingComments) {
|
|
1743
2466
|
return parent;
|
|
1744
2467
|
}
|
|
2468
|
+
// Skip now, if we find another function first,
|
|
2469
|
+
// there is no JSDoc for our function anymore.
|
|
2470
|
+
// Not interested in our start node if it didn't
|
|
2471
|
+
// contain leadingComments.
|
|
1745
2472
|
i--;
|
|
1746
2473
|
while (i >= 0) {
|
|
1747
2474
|
parent = parents[i];
|
|
2475
|
+
//if (parent.type === 'CallExpression') {
|
|
2476
|
+
// break;
|
|
2477
|
+
//}
|
|
1748
2478
|
if (nodeIsFunction(parent)) {
|
|
1749
2479
|
break;
|
|
1750
2480
|
}
|
|
@@ -1753,7 +2483,26 @@ class Asserter extends Stringifier {
|
|
|
1753
2483
|
}
|
|
1754
2484
|
i--;
|
|
1755
2485
|
}
|
|
1756
|
-
|
|
2486
|
+
/** @todo convert all files in test/typechecking/*.mjs into full unit tests */
|
|
2487
|
+
// Old way:
|
|
2488
|
+
// if (node.leadingComments) {
|
|
2489
|
+
// return node.leadingComments;
|
|
2490
|
+
// }
|
|
2491
|
+
// node = this.findParentOfType(node, 'ExpressionStatement');
|
|
2492
|
+
// if (!node) {
|
|
2493
|
+
// /**
|
|
2494
|
+
// * @todo Need more refactoring, see missing type-assertions in test/typechecking/good-old-es5.mjs
|
|
2495
|
+
// */
|
|
2496
|
+
// node = this.parents.findLast(_ => _.type === 'VariableDeclaration');
|
|
2497
|
+
// if (!node) {
|
|
2498
|
+
// return;
|
|
2499
|
+
// }
|
|
2500
|
+
// }
|
|
2501
|
+
}
|
|
2502
|
+
/**
|
|
2503
|
+
* @param {Node} node - The Babel AST node.
|
|
2504
|
+
* @returns {undefined | {}} The return value of `parseJSDoc`.
|
|
2505
|
+
*/
|
|
1757
2506
|
getJSDoc(node) {
|
|
1758
2507
|
if (node.type === 'BlockStatement') {
|
|
1759
2508
|
node = this.parent;
|
|
@@ -1761,10 +2510,12 @@ class Asserter extends Stringifier {
|
|
|
1761
2510
|
let {
|
|
1762
2511
|
leadingComments
|
|
1763
2512
|
} = node;
|
|
2513
|
+
// Receive the leadingComments from the ExpressionStatement, not the FunctionExpression itself.
|
|
1764
2514
|
if (node.type === 'FunctionExpression') {
|
|
1765
2515
|
const tmp = this.getLeadingCommentsNodeForFunctionExpression(node);
|
|
1766
2516
|
leadingComments = tmp == null ? void 0 : tmp.leadingComments;
|
|
1767
2517
|
}
|
|
2518
|
+
// Receive the leadingComments from ExportNamedDeclaration, if FunctionDeclaration has none
|
|
1768
2519
|
if (!leadingComments) {
|
|
1769
2520
|
if (node.type === 'FunctionDeclaration') {
|
|
1770
2521
|
const exportNamedDeclaration = this.findParentOfType(node, 'ExportNamedDeclaration');
|
|
@@ -1797,6 +2548,15 @@ class Asserter extends Stringifier {
|
|
|
1797
2548
|
}
|
|
1798
2549
|
}
|
|
1799
2550
|
}
|
|
2551
|
+
/**
|
|
2552
|
+
* Retrieves the name of a parameter from a Babel AST node.
|
|
2553
|
+
*
|
|
2554
|
+
* This function expects a node representing a function parameter and attempts to extract
|
|
2555
|
+
* the parameter's name directly or from an AssignmentPattern.
|
|
2556
|
+
*
|
|
2557
|
+
* @param {Node} param - The AST node representing the function parameter from which to extract the name.
|
|
2558
|
+
* @returns {string} The name of the parameter as a string, or the parameter's source code if the extraction fails.
|
|
2559
|
+
*/
|
|
1800
2560
|
getNameOfParam(param) {
|
|
1801
2561
|
if (param.type === 'Identifier') {
|
|
1802
2562
|
return param.name;
|
|
@@ -1815,19 +2575,29 @@ class Asserter extends Stringifier {
|
|
|
1815
2575
|
statsPrint() {
|
|
1816
2576
|
console.table(this.stats);
|
|
1817
2577
|
}
|
|
2578
|
+
/**
|
|
2579
|
+
* Retrieves statistical information for a given Babel AST node of this instance.
|
|
2580
|
+
*
|
|
2581
|
+
* @param {Node} node - The Babel AST node for which the statistical data is retrieved.
|
|
2582
|
+
* @returns {Stat} An object containing the statistical data for the specified node. If the
|
|
2583
|
+
* node type is unhandled, defaults to returning a dummy object with 'checked' and 'unchecked'
|
|
2584
|
+
* properties both set to 0.
|
|
2585
|
+
*/
|
|
1818
2586
|
getStatsForNode(node) {
|
|
1819
2587
|
const {
|
|
1820
2588
|
stats
|
|
1821
2589
|
} = this;
|
|
1822
2590
|
const type = nodeIsFunction(node) ? node.type : this.parentType;
|
|
1823
2591
|
if (type === 'ClassMethod') {
|
|
1824
|
-
const parent =
|
|
2592
|
+
const parent = /** @type {ClassMethod} */
|
|
2593
|
+
this.parent;
|
|
1825
2594
|
const {
|
|
1826
2595
|
kind
|
|
1827
2596
|
} = parent;
|
|
1828
2597
|
return stats[`ClassMethod#${kind}`];
|
|
1829
2598
|
} else if (type === 'ClassPrivateMethod') {
|
|
1830
|
-
const parent =
|
|
2599
|
+
const parent = /** @type {ClassPrivateMethod} */
|
|
2600
|
+
this.parent;
|
|
1831
2601
|
const {
|
|
1832
2602
|
kind
|
|
1833
2603
|
} = parent;
|
|
@@ -1843,6 +2613,17 @@ class Asserter extends Stringifier {
|
|
|
1843
2613
|
}
|
|
1844
2614
|
return stat;
|
|
1845
2615
|
}
|
|
2616
|
+
/**
|
|
2617
|
+
* Checks if a provided Babel AST node has a parameter with the given name.
|
|
2618
|
+
*
|
|
2619
|
+
* This function will look at the node's parameters if available and determine whether
|
|
2620
|
+
* one of them matches the provided name. Supports various parameter types such as Identifiers
|
|
2621
|
+
* and AssignmentPatterns.
|
|
2622
|
+
*
|
|
2623
|
+
* @param {Node} node - The Babel AST node to inspect. If it's a BlockStatement, the parent node is used instead.
|
|
2624
|
+
* @param {string} name - The name of the parameter to look for within the node's parameters.
|
|
2625
|
+
* @returns {boolean} True if the node has a parameter with the given name; false otherwise.
|
|
2626
|
+
*/
|
|
1846
2627
|
nodeHasParamName(node, name) {
|
|
1847
2628
|
if (node.type === 'BlockStatement') {
|
|
1848
2629
|
node = this.parent;
|
|
@@ -1878,6 +2659,18 @@ class Asserter extends Stringifier {
|
|
|
1878
2659
|
return false;
|
|
1879
2660
|
});
|
|
1880
2661
|
}
|
|
2662
|
+
/**
|
|
2663
|
+
* Generates a string containing type checks for a given Babel AST node based on associated JSDoc information.
|
|
2664
|
+
*
|
|
2665
|
+
* This function analyzes the node and its JSDoc annotations to construct runtime type
|
|
2666
|
+
* check expressions. It handles various parameter patterns and outputs code that performs
|
|
2667
|
+
* actual type assertions. If a node does not correspond to any known or supported pattern,
|
|
2668
|
+
* it returns an empty string.
|
|
2669
|
+
*
|
|
2670
|
+
* @override
|
|
2671
|
+
* @param {Node} node - The Babel AST node for which to generate type checks.
|
|
2672
|
+
* @returns {string} A string of code with type check assertions, based on the JSDoc comments associated with the given node.
|
|
2673
|
+
*/
|
|
1881
2674
|
generateTypeChecks(node) {
|
|
1882
2675
|
const {
|
|
1883
2676
|
parent
|
|
@@ -1886,6 +2679,7 @@ class Asserter extends Stringifier {
|
|
|
1886
2679
|
return '';
|
|
1887
2680
|
}
|
|
1888
2681
|
const jsdoc = this.getJSDoc(node);
|
|
2682
|
+
// return '// ' + JSON.stringify(jsdoc) + '\n';
|
|
1889
2683
|
const stat = this.getStatsForNode(node);
|
|
1890
2684
|
if (!jsdoc) {
|
|
1891
2685
|
stat.unchecked++;
|
|
@@ -1897,6 +2691,12 @@ class Asserter extends Stringifier {
|
|
|
1897
2691
|
} = this;
|
|
1898
2692
|
let out = '';
|
|
1899
2693
|
let first = true;
|
|
2694
|
+
const loc = this.getName(node);
|
|
2695
|
+
if (this.ignoreLocations.includes(loc)) {
|
|
2696
|
+
return '// IGNORE RTI TYPE VALIDATIONS, KNOWN ISSUES\n';
|
|
2697
|
+
}
|
|
2698
|
+
//out += `${spaces}/*${spaces} node.type=${node.type}\n${spaces}
|
|
2699
|
+
// ${JSON.stringify(jsdoc)}\n${parent}\n${spaces}*/\n`;
|
|
1900
2700
|
for (let name in jsdoc) {
|
|
1901
2701
|
const type = jsdoc[name];
|
|
1902
2702
|
const hasParam = this.nodeHasParamName(node, name);
|
|
@@ -1911,11 +2711,24 @@ class Asserter extends Stringifier {
|
|
|
1911
2711
|
const isObjectPattern = param.type === 'ObjectPattern';
|
|
1912
2712
|
const isArrayPattern = param.type === 'ArrayPattern';
|
|
1913
2713
|
const isSupportedPattern = isObjectPattern || isArrayPattern;
|
|
2714
|
+
// There are four kinds of patterns:
|
|
2715
|
+
// ObjectPattern:
|
|
2716
|
+
// function test({x = 123}) {return x;} test({x: 456});
|
|
2717
|
+
// ArrayPattern:
|
|
2718
|
+
// function test([x = 123]) {return x;}; test([456]);
|
|
2719
|
+
// AssignmentPattern made up of ObjectPattern:
|
|
2720
|
+
// function test({x = 123} = {}) {return x;} test();
|
|
2721
|
+
// AssignmentPattern made up of ArrayPattern:
|
|
2722
|
+
// function test([x = 123] = []) {return x;} test();
|
|
1914
2723
|
if (isSupportedPattern) {
|
|
2724
|
+
// The name doesn't matter any longer, because any pattern inherently
|
|
2725
|
+
// drops the identifier from the AST. But we can access it
|
|
2726
|
+
// via arguments[paramIndex] anyway.
|
|
1915
2727
|
name = `arguments[${paramIndex}]`;
|
|
1916
2728
|
} else if (param.type === 'AssignmentPattern') {
|
|
1917
2729
|
const _loc = this.getName(node);
|
|
1918
2730
|
if (param.left.type === 'ArrayPattern' && type.type === 'array') {
|
|
2731
|
+
// Add a type assertion for each element of the ArrayPattern
|
|
1919
2732
|
for (const element of param.left.elements) {
|
|
1920
2733
|
if (element.type !== 'Identifier') {
|
|
1921
2734
|
this.warn('Only Identifier case handled right now');
|
|
@@ -1927,6 +2740,7 @@ class Asserter extends Stringifier {
|
|
|
1927
2740
|
}
|
|
1928
2741
|
continue;
|
|
1929
2742
|
} else if (param.left.type === 'ObjectPattern' && type.type === 'object') {
|
|
2743
|
+
// Add a type assertion for each property of the ObjectPattern
|
|
1930
2744
|
for (const property of param.left.properties) {
|
|
1931
2745
|
if (property.key.type !== 'Identifier') {
|
|
1932
2746
|
this.warn('ObjectPattern> Only Identifier case handled right now');
|
|
@@ -1965,27 +2779,39 @@ class Asserter extends Stringifier {
|
|
|
1965
2779
|
}
|
|
1966
2780
|
t = '"' + this.toSource(classDecl.id) + '"';
|
|
1967
2781
|
}
|
|
1968
|
-
const
|
|
2782
|
+
const _loc3 = this.getName(node);
|
|
1969
2783
|
let prevCheck = '';
|
|
1970
|
-
|
|
2784
|
+
// JSDoc doesn't support multiple function signatures yet, but this is
|
|
2785
|
+
// exactly what we would need to deal with ObjectPool'ing
|
|
2786
|
+
if (_loc3 === 'ContactPoint#constructor' || _loc3 === 'ContactResult#constructor' || _loc3 === 'SingleContactResult#constructor') {
|
|
1971
2787
|
prevCheck = 'arguments.length !== 0 && ';
|
|
1972
2788
|
}
|
|
1973
2789
|
if (first) {
|
|
1974
2790
|
out += '\n';
|
|
1975
2791
|
first = false;
|
|
1976
2792
|
}
|
|
1977
|
-
out += `${spaces}if (${prevCheck}!assertType(${name}, ${t}, '${
|
|
2793
|
+
out += `${spaces}if (${prevCheck}!assertType(${name}, ${t}, '${_loc3}', '${name}')) {\n`;
|
|
1978
2794
|
out += `${spaces} youCanAddABreakpointHere();\n${spaces}}\n`;
|
|
1979
2795
|
}
|
|
1980
2796
|
return out;
|
|
1981
2797
|
}
|
|
2798
|
+
/**
|
|
2799
|
+
* @param {Node} node - The Babel AST node.
|
|
2800
|
+
* @returns {string} Best possible human-readable name of given node.
|
|
2801
|
+
*/
|
|
1982
2802
|
getNameForFunctionExpression(node) {
|
|
1983
2803
|
const objectProperty = this.findParentOfType(node, 'ObjectProperty');
|
|
1984
2804
|
if (objectProperty) {
|
|
2805
|
+
// See good-old-es5.mjs example for a test case
|
|
2806
|
+
// TODO: Make an even better name based on Object.assign(ScopeSpace.prototype
|
|
2807
|
+
// Ideally we would figure out the name: ScopeSpace#resolve
|
|
2808
|
+
// Currently we only find "resolve" (still better than 'unnamed function expression'...)
|
|
1985
2809
|
return this.toSource(objectProperty.key);
|
|
1986
2810
|
}
|
|
1987
2811
|
const expressionStatement = this.findParentOfType(node, 'ExpressionStatement');
|
|
1988
2812
|
if (expressionStatement) {
|
|
2813
|
+
// There are many kinds of expressions
|
|
2814
|
+
// type Expression = ArrayExpression | AssignmentExpression | BinaryExpression | CallExpression | ...
|
|
1989
2815
|
const {
|
|
1990
2816
|
left
|
|
1991
2817
|
} = expressionStatement.expression;
|
|
@@ -1996,6 +2822,10 @@ class Asserter extends Stringifier {
|
|
|
1996
2822
|
}
|
|
1997
2823
|
return 'unnamed function expression';
|
|
1998
2824
|
}
|
|
2825
|
+
/**
|
|
2826
|
+
* @param {Node} node - The Babel AST node.
|
|
2827
|
+
* @returns {string} Stringification of the node.
|
|
2828
|
+
*/
|
|
1999
2829
|
getName(node) {
|
|
2000
2830
|
const toSource = this.toSource.bind(this);
|
|
2001
2831
|
if (node.type === 'BlockStatement') {
|
|
@@ -2036,6 +2866,11 @@ class Asserter extends Stringifier {
|
|
|
2036
2866
|
return '/*MISSING*/';
|
|
2037
2867
|
}
|
|
2038
2868
|
}
|
|
2869
|
+
/**
|
|
2870
|
+
* @override
|
|
2871
|
+
* @param {import("@babel/types").BinaryExpression} node - The Babel AST node.
|
|
2872
|
+
* @returns {string} Stringification of the node.
|
|
2873
|
+
*/
|
|
2039
2874
|
BinaryExpression(node) {
|
|
2040
2875
|
if (!this.validateDivision) {
|
|
2041
2876
|
return super.BinaryExpression(node);
|
|
@@ -2052,9 +2887,15 @@ class Asserter extends Stringifier {
|
|
|
2052
2887
|
}
|
|
2053
2888
|
return `${left_} ${operator} ${right_}`;
|
|
2054
2889
|
}
|
|
2890
|
+
/**
|
|
2891
|
+
* @override
|
|
2892
|
+
* @param {import("@babel/types").File} node - The Babel AST node.
|
|
2893
|
+
* @returns {string} Stringification of the node.
|
|
2894
|
+
*/
|
|
2055
2895
|
File(node) {
|
|
2896
|
+
// @todo figure out why errors is in Babel node and not in @babel/types...
|
|
2056
2897
|
const {
|
|
2057
|
-
program,
|
|
2898
|
+
/*errors,*/program,
|
|
2058
2899
|
comments
|
|
2059
2900
|
} = node;
|
|
2060
2901
|
if (comments) {
|
|
@@ -2063,6 +2904,7 @@ class Asserter extends Stringifier {
|
|
|
2063
2904
|
parseJSDocTypedef(this.typedefs, warn, comment, this.expandType);
|
|
2064
2905
|
}
|
|
2065
2906
|
}
|
|
2907
|
+
//console.log("this.typedefs", this.typedefs);
|
|
2066
2908
|
let out = '';
|
|
2067
2909
|
for (const name in this.typedefs) {
|
|
2068
2910
|
const typedef = this.typedefs[name];
|
|
@@ -2075,7 +2917,24 @@ class Asserter extends Stringifier {
|
|
|
2075
2917
|
}
|
|
2076
2918
|
}
|
|
2077
2919
|
|
|
2920
|
+
/**
|
|
2921
|
+
* Simple facade which does all the processing. Processes the input
|
|
2922
|
+
* source string, adding runtime type checks based on JSDoc comments.
|
|
2923
|
+
*
|
|
2924
|
+
* This function takes JavaScript source code as input, parses it to an AST, traverses the
|
|
2925
|
+
* AST to find type annotations in JSDoc comments, and generates appropriate runtime type
|
|
2926
|
+
* assertions. These are then inserted into the source, producing a new version of the code
|
|
2927
|
+
* that includes runtime type checking based on the original JSDoc annotations.
|
|
2928
|
+
*
|
|
2929
|
+
* @param {string} src - The input source code containing JSDoc comments to be processed
|
|
2930
|
+
* for type checks.
|
|
2931
|
+
* @param {import('./Asserter.mjs').Options} [options] - Configuration options that dictate
|
|
2932
|
+
* how the processing is performed.
|
|
2933
|
+
* @returns {string} The transformed source code with inserted runtime type checks, or the
|
|
2934
|
+
* original source code commented with an error if processing fails.
|
|
2935
|
+
*/
|
|
2078
2936
|
function addTypeChecks(src, options) {
|
|
2937
|
+
console.log("GOT OPTIONS", options);
|
|
2079
2938
|
try {
|
|
2080
2939
|
const asserter = new Asserter(options);
|
|
2081
2940
|
const ast = parse(src, {
|
|
@@ -2090,28 +2949,45 @@ function addTypeChecks(src, options) {
|
|
|
2090
2949
|
}
|
|
2091
2950
|
}
|
|
2092
2951
|
|
|
2952
|
+
/**
|
|
2953
|
+
* @param {object} ast - The Babel AST.
|
|
2954
|
+
* @returns {string} String representation in JSON format for debugging/inspecting the AST.
|
|
2955
|
+
*/
|
|
2093
2956
|
function ast2json(ast) {
|
|
2094
2957
|
return JSON.stringify(ast, function (name, val) {
|
|
2095
2958
|
if (name === "loc" || name === "start" || name === "end") {
|
|
2096
|
-
return;
|
|
2959
|
+
return; // remove
|
|
2097
2960
|
}
|
|
2098
|
-
|
|
2961
|
+
|
|
2962
|
+
return val; // keep
|
|
2099
2963
|
}, 2);
|
|
2100
2964
|
}
|
|
2101
2965
|
|
|
2102
2966
|
const drop = ['loc', 'start', 'end', 'leadingComments', 'trailingComments', 'innerComments', 'innerComments', 'comments'];
|
|
2967
|
+
/**
|
|
2968
|
+
* @example
|
|
2969
|
+
* setRight(ast2jsonForComparison(parseSync("/** *"))); // Close comment with / after last *
|
|
2970
|
+
* @param {object} ast - The Babel AST.
|
|
2971
|
+
* @returns {string} String representation in JSON format for debugging/inspecting the AST.
|
|
2972
|
+
*/
|
|
2103
2973
|
function ast2jsonForComparison(ast) {
|
|
2104
2974
|
return JSON.stringify(ast, function (name, val) {
|
|
2105
2975
|
if (name === 'trailingComma' || name === 'parenStart') {
|
|
2106
2976
|
return 'offset removed for better comparison';
|
|
2107
2977
|
}
|
|
2108
2978
|
if (drop.includes(name)) {
|
|
2109
|
-
return undefined;
|
|
2979
|
+
return undefined; // remove
|
|
2110
2980
|
}
|
|
2111
|
-
|
|
2981
|
+
|
|
2982
|
+
return val; // keep
|
|
2112
2983
|
}, 2);
|
|
2113
2984
|
}
|
|
2114
2985
|
|
|
2986
|
+
/**
|
|
2987
|
+
* A roundtrip between code -> AST -> code to validate Stringifier.
|
|
2988
|
+
* @param {string} code - The code.
|
|
2989
|
+
* @returns {string | undefined} The new and once parsed and stringified code.
|
|
2990
|
+
*/
|
|
2115
2991
|
function code2ast2code(code) {
|
|
2116
2992
|
const stringifier = new Stringifier();
|
|
2117
2993
|
const ast = parse(code, {
|
|
@@ -2124,6 +3000,11 @@ function code2ast2code(code) {
|
|
|
2124
3000
|
return out;
|
|
2125
3001
|
}
|
|
2126
3002
|
|
|
3003
|
+
/**
|
|
3004
|
+
* @param {string} left - Left source code.
|
|
3005
|
+
* @param {string} right - Right source code.
|
|
3006
|
+
* @returns {boolean} Whether source codes are identical on the AST level.
|
|
3007
|
+
*/
|
|
2127
3008
|
function compareAST(left, right) {
|
|
2128
3009
|
const l = parse(left, {
|
|
2129
3010
|
sourceType: 'module'
|
|
@@ -2137,19 +3018,70 @@ function compareAST(left, right) {
|
|
|
2137
3018
|
return test;
|
|
2138
3019
|
}
|
|
2139
3020
|
|
|
3021
|
+
/**
|
|
3022
|
+
* Transforms a type string into a structured type representation.
|
|
3023
|
+
*
|
|
3024
|
+
* This function parses a given type string and converts it into a TypeScript
|
|
3025
|
+
* Abstract Syntax Tree (AST), then uses that AST to return a structured type
|
|
3026
|
+
* representation that can be further utilized or interpreted.
|
|
3027
|
+
*
|
|
3028
|
+
* @todo Better handling of weird case: Array<>
|
|
3029
|
+
* @example
|
|
3030
|
+
* const {expandType} = await import("./src-transpiler/expandType.mjs");
|
|
3031
|
+
* expandType('[string, Array|AnyTypedArray, number[]]|[ONNXTensor]');
|
|
3032
|
+
* expandType('(123) '); // Outputs: '123'
|
|
3033
|
+
* expandType(' ( ( 123 ) ) '); // Outputs: '123'
|
|
3034
|
+
* expandType('Array<number> '); // Outputs: {type: 'array', elementType: 'number'}
|
|
3035
|
+
* expandType('Array<(123) > '); // Outputs: {type: 'array', elementType: '123'}
|
|
3036
|
+
* expandType('Array<"abc" | 123> '); // Outputs: {type: 'array', elementType: {type: 'union', members: ['"abc"', '123']}}
|
|
3037
|
+
* expandType(' (string ) |(number ) '); // Outputs: {type: 'union', members: [ 'string', 'number']}
|
|
3038
|
+
* expandType(' "apples" | ( "bananas") '); // Outputs: {type: 'union', members: [ '"apples"', '"bananas"']}
|
|
3039
|
+
* expandType('123? '); // Outputs: {"type":"union","members":["123","null"]}
|
|
3040
|
+
* expandType('123|null '); // Outputs: {"type":"union","members":["123","null"]}
|
|
3041
|
+
* expandType('Map<string, any> '); // Outputs:
|
|
3042
|
+
* expandType('typeof Number '); // Outputs:
|
|
3043
|
+
* @param {string} type - The type string to be expanded into a structured representation.
|
|
3044
|
+
* @todo Share type with expandTypeBabelTS and expandTypeDepFree
|
|
3045
|
+
* @returns {string | {type: string, [key: string]: any} | undefined} The structured type
|
|
3046
|
+
* representation obtained from parsing and converting the provided type string.
|
|
3047
|
+
*/
|
|
2140
3048
|
function expandType(type) {
|
|
2141
3049
|
const ast = parseType(type);
|
|
2142
3050
|
return toSourceTS(ast);
|
|
2143
3051
|
}
|
|
3052
|
+
/**
|
|
3053
|
+
* @todo I want to use for example: import('typescript').Node
|
|
3054
|
+
* But the TS types make no sense to me so far ... need to investigate more.
|
|
3055
|
+
* @typedef TypeScriptType
|
|
3056
|
+
* @property {object[]|undefined} typeArguments - The type arguments.
|
|
3057
|
+
* @property {import('typescript').Node} typeName - The type name.
|
|
3058
|
+
* @property {number} kind - The kind for `ts.SyntaxKind[kind]`.
|
|
3059
|
+
*/
|
|
3060
|
+
/**
|
|
3061
|
+
* @param {string} str - The type string.
|
|
3062
|
+
* @returns {TypeScriptType} - The node containing all the information about the input type string.
|
|
3063
|
+
*/
|
|
2144
3064
|
function parseType(str) {
|
|
3065
|
+
// TS doesn't like ... notation in this context
|
|
2145
3066
|
if (str.startsWith('...')) {
|
|
2146
|
-
str = str.slice(3);
|
|
2147
|
-
str += '[]';
|
|
3067
|
+
str = str.slice(3); // remove dots
|
|
3068
|
+
str += '[]'; // turn into array
|
|
2148
3069
|
}
|
|
3070
|
+
// type tmp = (...string) => 123; to have a function context
|
|
2149
3071
|
str = `type tmp = ${str};`;
|
|
2150
|
-
const ast = ts.createSourceFile('repl.ts', str, ts.ScriptTarget.Latest, true);
|
|
3072
|
+
const ast = ts.createSourceFile('repl.ts', str, ts.ScriptTarget.Latest, true /*setParentNodes*/);
|
|
2151
3073
|
return ast.statements[0].type;
|
|
2152
3074
|
}
|
|
3075
|
+
/**
|
|
3076
|
+
* Converts a TypeScript AST node to a source string representation or to an intermediate object describing the type.
|
|
3077
|
+
*
|
|
3078
|
+
* This function handles various TypeScript AST node types and converts them into a string
|
|
3079
|
+
* or an object representing the type.
|
|
3080
|
+
*
|
|
3081
|
+
* @param {TypeScriptType} node - The TypeScript AST node to convert.
|
|
3082
|
+
* @returns {string | {type: string, [key: string]: any} | undefined} The source string or an object with type information based on the node,
|
|
3083
|
+
* or `undefined` if the node kind is not handled.
|
|
3084
|
+
*/
|
|
2153
3085
|
function toSourceTS(node) {
|
|
2154
3086
|
const {
|
|
2155
3087
|
typeArguments,
|
|
@@ -2193,23 +3125,31 @@ function toSourceTS(node) {
|
|
|
2193
3125
|
IndexedAccessType,
|
|
2194
3126
|
RestType,
|
|
2195
3127
|
TypeQuery,
|
|
3128
|
+
// parseType('typeof Number')
|
|
2196
3129
|
TypeOperator,
|
|
3130
|
+
// parseType('keyof typeof obj')
|
|
2197
3131
|
KeyOfKeyword,
|
|
3132
|
+
// "operator" key in TypeOperator node
|
|
2198
3133
|
ConstructorType,
|
|
3134
|
+
// parseType('new (...args: any[]) => any');
|
|
2199
3135
|
NamedTupleMember
|
|
2200
3136
|
} = ts.SyntaxKind;
|
|
3137
|
+
// console.log({typeArguments, typeName, kind_, node});
|
|
2201
3138
|
switch (node.kind) {
|
|
2202
3139
|
case BigIntKeyword:
|
|
2203
3140
|
return {
|
|
2204
3141
|
type: 'bigint'
|
|
2205
3142
|
};
|
|
2206
3143
|
case BigIntLiteral:
|
|
2207
|
-
const literal = node.text.slice(0, -1);
|
|
3144
|
+
const literal = node.text.slice(0, -1); // Remove the "n"
|
|
2208
3145
|
return {
|
|
2209
3146
|
type: 'bigint',
|
|
2210
3147
|
literal
|
|
2211
3148
|
};
|
|
2212
3149
|
case ConditionalType:
|
|
3150
|
+
// Keys on node:
|
|
3151
|
+
// ['pos', 'end', 'flags', 'modifierFlagsCache', 'transformFlags', 'parent', 'kind', 'checkType',
|
|
3152
|
+
// 'extendsType', 'trueType', 'falseType', 'locals', 'nextContainer']
|
|
2213
3153
|
const checkType = toSourceTS(node.checkType);
|
|
2214
3154
|
const extendsType = toSourceTS(node.extendsType);
|
|
2215
3155
|
const trueType = toSourceTS(node.trueType);
|
|
@@ -2257,6 +3197,8 @@ function toSourceTS(node) {
|
|
|
2257
3197
|
type: 'union',
|
|
2258
3198
|
members: [t, 'null']
|
|
2259
3199
|
};
|
|
3200
|
+
// todo work out more: const jsdoc = `(...a: ...number) => 123
|
|
3201
|
+
// TS even thinks it's two parameters... just go for array/[]
|
|
2260
3202
|
case Parameter:
|
|
2261
3203
|
const type = node.type ? toSourceTS(node.type) : 'any';
|
|
2262
3204
|
const name = toSourceTS(node.name);
|
|
@@ -2389,6 +3331,7 @@ function toSourceTS(node) {
|
|
|
2389
3331
|
return toSourceTS(node.literal);
|
|
2390
3332
|
case AnyKeyword:
|
|
2391
3333
|
case BooleanKeyword:
|
|
3334
|
+
// ts.SyntaxKind[parseType("*").kind] === 'JSDocAllType'
|
|
2392
3335
|
case JSDocAllType:
|
|
2393
3336
|
case NullKeyword:
|
|
2394
3337
|
case NumericLiteral:
|
|
@@ -2407,10 +3350,17 @@ function toSourceTS(node) {
|
|
|
2407
3350
|
properties: {}
|
|
2408
3351
|
};
|
|
2409
3352
|
case ParenthesizedType:
|
|
3353
|
+
// fall-through for parentheses
|
|
2410
3354
|
return toSourceTS(node.type);
|
|
2411
3355
|
case LastTypeNode:
|
|
2412
3356
|
return toSourceTS(node.qualifier);
|
|
2413
3357
|
default:
|
|
3358
|
+
// const test = {};
|
|
3359
|
+
// Object.entries(ts.SyntaxKind).forEach(([name, id]) => {
|
|
3360
|
+
// test[id] = (test[id] || []);
|
|
3361
|
+
// test[id].push(name);
|
|
3362
|
+
// });
|
|
3363
|
+
// console.log(test);
|
|
2414
3364
|
console.warn('toSourceTS> unhandled kind - make sure to understand you cannot reverse TS enums');
|
|
2415
3365
|
console.warn('if they contain range aliases, for example:');
|
|
2416
3366
|
console.warn('ts.SyntaxKind.NumericLiteral === ts.SyntaxKind.FirstLiteralToken');
|
|
@@ -2419,17 +3369,58 @@ function toSourceTS(node) {
|
|
|
2419
3369
|
}
|
|
2420
3370
|
}
|
|
2421
3371
|
|
|
3372
|
+
/**
|
|
3373
|
+
* @todo Better handling of weird case: Array<>
|
|
3374
|
+
* @example
|
|
3375
|
+
* const {expandTypeBabelTS} = await import("./src-transpiler/expandTypeBabelTS.mjs");
|
|
3376
|
+
* expandTypeBabelTS('[string, Array|AnyTypedArray, number[]]|[ONNXTensor]');
|
|
3377
|
+
* expandTypeBabelTS('(123) '); // Outputs: '123'
|
|
3378
|
+
* expandTypeBabelTS(' ( ( 123 ) ) '); // Outputs: '123'
|
|
3379
|
+
* expandTypeBabelTS('Array<number> '); // Outputs: {type: 'array', elementType: 'number'}
|
|
3380
|
+
* expandTypeBabelTS('Array<(123) > '); // Outputs: {type: 'array', elementType: '123'}
|
|
3381
|
+
* expandTypeBabelTS('Array<"abc" | 123> '); // Outputs: {type: 'array', elementType: {type: 'union', members: ['"abc"', '123']}}
|
|
3382
|
+
* expandTypeBabelTS(' (string ) |(number ) '); // Outputs: {type: 'union', members: [ 'string', 'number']}
|
|
3383
|
+
* expandTypeBabelTS(' "apples" | ( "bananas") '); // Outputs: {type: 'union', members: [ '"apples"', '"bananas"']}
|
|
3384
|
+
* expandTypeBabelTS('123? '); // Outputs: {type: 'union', members: ['123', 'null']}
|
|
3385
|
+
* expandTypeBabelTS('123|null '); // Outputs: {type: 'union', members: ['123', 'null']}
|
|
3386
|
+
* expandTypeBabelTS('Map<string, any> '); // Outputs: {type: 'map', key: 'string', val: 'any'}
|
|
3387
|
+
* expandTypeBabelTS("(a: number, b: number) => number")
|
|
3388
|
+
* @param {string} type - The input type.
|
|
3389
|
+
* @returns {string|object|undefined} - See `toSourceBabelTS`.
|
|
3390
|
+
*/
|
|
2422
3391
|
function expandTypeBabelTS(type) {
|
|
2423
3392
|
const ast = parseTypeBabelTS(type);
|
|
2424
3393
|
return toSourceBabelTS(ast);
|
|
2425
3394
|
}
|
|
3395
|
+
/**
|
|
3396
|
+
* @param {string} str - The type string.
|
|
3397
|
+
* @returns {import('@babel/types').Node} - The node containing all the information about the input type string.
|
|
3398
|
+
*/
|
|
2426
3399
|
function parseTypeBabelTS(str) {
|
|
3400
|
+
// TS doesn't like ... notation in this context
|
|
3401
|
+
//if (str.startsWith('...')) {
|
|
3402
|
+
// str = str.slice(3); // remove dots
|
|
3403
|
+
// str += '[]'; // turn into array
|
|
3404
|
+
//}
|
|
3405
|
+
// type tmp = (...string) => 123; to have a function context
|
|
2427
3406
|
str = `type tmp = ${str};`;
|
|
2428
3407
|
const ast = parse(str, {
|
|
2429
3408
|
plugins: ['typescript']
|
|
2430
3409
|
});
|
|
2431
3410
|
return ast.program.body[0].typeAnnotation;
|
|
2432
3411
|
}
|
|
3412
|
+
/**
|
|
3413
|
+
* Converts a Babel AST node to its source string representation or structured type object.
|
|
3414
|
+
*
|
|
3415
|
+
* This function handles a variety of node types provided by Babel and converts them into a string
|
|
3416
|
+
* or an intermediate object representing the type, depending on the complexity of the type described by the node.
|
|
3417
|
+
*
|
|
3418
|
+
* @param {import('@babel/types').Node} node - The Babel AST node to convert.
|
|
3419
|
+
* @returns {string|object|undefined} - A string, object representing a structured type, or `undefined` for unhandled types.
|
|
3420
|
+
* Depending on the node, it may return a simple type string (e.g., `"string"` for `TSStringKeyword`),
|
|
3421
|
+
* a structured type object (e.g., a record type for `TSTypeReference` with type arguments),
|
|
3422
|
+
* or `undefined` if the encountered type is not handled. Unhandled types trigger a warning and enter a debugger statement.
|
|
3423
|
+
*/
|
|
2433
3424
|
function toSourceBabelTS(node) {
|
|
2434
3425
|
switch (node.type) {
|
|
2435
3426
|
case 'TSBigIntKeyword':
|
|
@@ -2442,10 +3433,34 @@ function toSourceBabelTS(node) {
|
|
|
2442
3433
|
type: 'bigint',
|
|
2443
3434
|
literal
|
|
2444
3435
|
};
|
|
3436
|
+
// expandTypeBabelTS("(a: number, b: number) => number")
|
|
3437
|
+
/**
|
|
3438
|
+
* @todo the parameters are given as identifiers with "typeAnnotation"
|
|
3439
|
+
*/
|
|
3440
|
+
// case 'TSFunctionType':
|
|
3441
|
+
// const parameters = node.parameters.map(toSourceBabelTS);
|
|
3442
|
+
// // I wish Babel AST would be like tsc AST here:
|
|
3443
|
+
// // parseType("(a: number, b: number) => number").parameters[0].kind === ts.SyntaxKind.Parameter
|
|
3444
|
+
// return {type: 'function', parameters};
|
|
3445
|
+
// Fix first: https://github.com/babel/babel/issues/16073
|
|
3446
|
+
//case 'JSDocNullableType':
|
|
3447
|
+
// const t = toSourceBabelTS(node.type);
|
|
3448
|
+
// return {type: 'union', members: [t, 'null']};
|
|
3449
|
+
// todo work out more: const jsdoc = `(...a: ...number) => 123
|
|
3450
|
+
// TS even thinks it's two parameters... just go for array/[]
|
|
3451
|
+
//case 'Parameter':
|
|
3452
|
+
// const type = node.type ? toSourceBabelTS(node.type) : 'any';
|
|
3453
|
+
// const name = toSourceBabelTS(node.name);
|
|
3454
|
+
// const ret = {type, name};
|
|
3455
|
+
// if (node.dotDotDotToken) {
|
|
3456
|
+
// return {type: 'array', elementType: ret};
|
|
3457
|
+
// }
|
|
3458
|
+
// return ret;
|
|
2445
3459
|
case 'TSTypeReference':
|
|
2446
3460
|
{
|
|
2447
3461
|
const name = toSourceBabelTS(node.typeName);
|
|
2448
3462
|
if (!node.typeParameters) {
|
|
3463
|
+
// console.log(`node.typeName.name=${node.typeName.name} name=${name}`, node);
|
|
2449
3464
|
return node.typeName.name;
|
|
2450
3465
|
}
|
|
2451
3466
|
console.assert(node.typeParameters.type === 'TSTypeParameterInstantiation');
|
|
@@ -2529,6 +3544,9 @@ function toSourceBabelTS(node) {
|
|
|
2529
3544
|
type: 'object',
|
|
2530
3545
|
properties
|
|
2531
3546
|
};
|
|
3547
|
+
// case 'PropertySignature':
|
|
3548
|
+
// console.warn('toSourceBabelTS> should not happen, handled by TypeLiteral directly');
|
|
3549
|
+
// return `${toSourceBabelTS(node.name)}: ${toSourceBabelTS(node.type)}`;
|
|
2532
3550
|
case 'Identifier':
|
|
2533
3551
|
return node.name;
|
|
2534
3552
|
case 'TSArrayType':
|
|
@@ -2538,25 +3556,36 @@ function toSourceBabelTS(node) {
|
|
|
2538
3556
|
};
|
|
2539
3557
|
case 'TSLiteralType':
|
|
2540
3558
|
return toSourceBabelTS(node.literal);
|
|
3559
|
+
// expandTypeBabelTS("any")
|
|
2541
3560
|
case 'TSAnyKeyword':
|
|
2542
3561
|
return 'any';
|
|
3562
|
+
// expandTypeBabelTS("boolean")
|
|
2543
3563
|
case 'TSBooleanKeyword':
|
|
2544
3564
|
return 'boolean';
|
|
3565
|
+
// expandTypeBabelTS('true | false');
|
|
2545
3566
|
case 'BooleanLiteral':
|
|
2546
3567
|
return node.value.toString();
|
|
3568
|
+
// ts.SyntaxKind[parseType("*").kind] === 'JSDocAllType'
|
|
3569
|
+
// But Babel-TS doesn't parse it atm
|
|
3570
|
+
//case 'JSDocAllType':
|
|
3571
|
+
// expandTypeBabelTS('null')
|
|
2547
3572
|
case 'TSNullKeyword':
|
|
2548
3573
|
return 'null';
|
|
3574
|
+
// expandTypeBabelTS('123')
|
|
2549
3575
|
case 'NumericLiteral':
|
|
2550
3576
|
case 'StringLiteral':
|
|
2551
3577
|
return node.extra.raw;
|
|
3578
|
+
// expandTypeBabelTS('undefined')
|
|
2552
3579
|
case 'TSUndefinedKeyword':
|
|
2553
3580
|
return 'undefined';
|
|
2554
3581
|
case 'TSUnknownKeyword':
|
|
2555
3582
|
return 'unknown';
|
|
2556
3583
|
case 'TSNeverKeyword':
|
|
2557
3584
|
return 'never';
|
|
3585
|
+
// parseTypeBabelTS('void');
|
|
2558
3586
|
case 'TSVoidKeyword':
|
|
2559
3587
|
return 'void';
|
|
3588
|
+
// expandTypeBabelTS('this')
|
|
2560
3589
|
case 'TSThisType':
|
|
2561
3590
|
return 'this';
|
|
2562
3591
|
case 'ObjectKeyword':
|
|
@@ -2565,6 +3594,7 @@ function toSourceBabelTS(node) {
|
|
|
2565
3594
|
properties: {}
|
|
2566
3595
|
};
|
|
2567
3596
|
case 'ParenthesizedType':
|
|
3597
|
+
// fall-through for parentheses
|
|
2568
3598
|
return toSourceBabelTS(node.type);
|
|
2569
3599
|
case 'LastTypeNode':
|
|
2570
3600
|
return toSourceBabelTS(node.qualifier);
|