@vesk/prettier-plugin 0.0.1

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/src/print.js ADDED
@@ -0,0 +1,1382 @@
1
+ import { doc } from 'prettier';
2
+
3
+ const { builders: b, utils } = doc;
4
+ const {
5
+ group,
6
+ indent,
7
+ hardline,
8
+ softline,
9
+ line,
10
+ literalline,
11
+ join,
12
+ fill,
13
+ ifBreak,
14
+ lineSuffix,
15
+ breakParent,
16
+ conditionalGroup,
17
+ indentIfBreak,
18
+ } = b;
19
+ const { replaceEndOfLine, stripTrailingHardline, willBreak } = utils;
20
+
21
+ // ── Helpers ─────────────────────────────────────────────────────
22
+
23
+ function semi(options) {
24
+ return options.semi ? ';' : '';
25
+ }
26
+
27
+ function printComment(comment) {
28
+ return comment.type === 'Line' ? '//' + comment.value : '/*' + comment.value + '*/';
29
+ }
30
+
31
+ function quoteString(str, single) {
32
+ const quote = single ? "'" : '"';
33
+ const out = [];
34
+ for (const ch of str) {
35
+ if (ch === quote) out.push('\\' + ch);
36
+ else if (ch === '\\') out.push('\\\\');
37
+ else if (ch === '\n') out.push('\\n');
38
+ else if (ch === '\r') out.push('\\r');
39
+ else if (ch === '\t') out.push('\\t');
40
+ else if (ch === '\u2028') out.push('\\u2028');
41
+ else if (ch === '\u2029') out.push('\\u2029');
42
+ else out.push(ch);
43
+ }
44
+ return quote + out.join('') + quote;
45
+ }
46
+
47
+ function hasBlankLineBetween(prevNode, nextNode) {
48
+ return (
49
+ prevNode &&
50
+ nextNode &&
51
+ prevNode.loc &&
52
+ nextNode.loc &&
53
+ nextNode.loc.start.line - prevNode.loc.end.line >= 2
54
+ );
55
+ }
56
+
57
+ function wasOriginallySingleLine(node) {
58
+ return node.loc && node.loc.start.line === node.loc.end.line;
59
+ }
60
+
61
+ const PRECEDENCE = {
62
+ '||': 1,
63
+ '??': 1,
64
+ '&&': 2,
65
+ '|': 3,
66
+ '^': 4,
67
+ '&': 5,
68
+ '==': 6,
69
+ '!=': 6,
70
+ '===': 6,
71
+ '!==': 6,
72
+ '<': 7,
73
+ '<=': 7,
74
+ '>': 7,
75
+ '>=': 7,
76
+ in: 7,
77
+ instanceof: 7,
78
+ '<<': 8,
79
+ '>>': 8,
80
+ '>>>': 8,
81
+ '+': 9,
82
+ '-': 9,
83
+ '*': 10,
84
+ '/': 10,
85
+ '%': 10,
86
+ '**': 11,
87
+ };
88
+
89
+ function binaryPrecedence(node) {
90
+ return node.operator === 'in' || node.operator === 'instanceof'
91
+ ? 7
92
+ : node.operator === '??'
93
+ ? 1
94
+ : PRECEDENCE[node.operator] ?? 1;
95
+ }
96
+
97
+ function needsBinaryParens(child, operator, side) {
98
+ if (!child || child.type !== 'BinaryExpression') return false;
99
+ const childPrec = binaryPrecedence(child);
100
+ const parentPrec = PRECEDENCE[operator] ?? 1;
101
+ if (childPrec < parentPrec) return true;
102
+ if (childPrec === parentPrec) {
103
+ if (operator === '**') return side === 'left';
104
+ if (operator === '??' || child.operator === '??') return true;
105
+ return side === 'right';
106
+ }
107
+ return false;
108
+ }
109
+
110
+ // ── Statement list printing ─────────────────────────────────────
111
+
112
+ function printStatementList(docs, nodes, options) {
113
+ const out = [];
114
+ for (let i = 0; i < docs.length; i++) {
115
+ out.push(docs[i]);
116
+ if (i < docs.length - 1) {
117
+ out.push(hasBlankLineBetween(nodes[i], nodes[i + 1]) ? [hardline, hardline] : hardline);
118
+ }
119
+ }
120
+ return out;
121
+ }
122
+
123
+ function printBlock(node, path, options, print) {
124
+ if (!node.body.length) return '{}';
125
+ const docs = path.map(print, 'body');
126
+ return group([
127
+ '{',
128
+ indent([hardline, ...printStatementList(docs, node.body, options)]),
129
+ hardline,
130
+ '}',
131
+ ]);
132
+ }
133
+
134
+ function printFunctionParams(node, path, options, print) {
135
+ if (!node.params || node.params.length === 0) return '()';
136
+ return printParams(node, path, options, print);
137
+ }
138
+
139
+ function printParams(node, path, options, print) {
140
+ const params = node.params || [];
141
+ if (params.length === 0) return '';
142
+ const docs = path.map(print, 'params');
143
+ const typeParams = node.typeParameters ? path.call(print, 'typeParameters') : '';
144
+ return group([
145
+ typeParams ? ['<', typeParams, '>'] : '',
146
+ '(',
147
+ indent([softline, join([',', line], docs)]),
148
+ ifBreak(options.trailingComma === 'none' ? '' : ','),
149
+ softline,
150
+ ')',
151
+ node.returnType ? path.call(print, 'returnType') : '',
152
+ ]);
153
+ }
154
+
155
+ function printCallArguments(args, path, options, print) {
156
+ if (args.length === 0) return '()';
157
+ const docs = path.map(print, 'arguments');
158
+ return group([
159
+ '(',
160
+ indent([softline, join([',', line], docs)]),
161
+ ifBreak(options.trailingComma === 'none' ? '' : ','),
162
+ softline,
163
+ ')',
164
+ ]);
165
+ }
166
+
167
+ function printStringLiteral(value, options) {
168
+ return quoteString(value, options.singleQuote);
169
+ }
170
+
171
+ function printLiteral(node, options) {
172
+ if (node.value === null) return 'null';
173
+ if (typeof node.value === 'string') return printStringLiteral(node.value, options);
174
+ if (typeof node.value === 'bigint') return (node.bigint ?? String(node.value)) + 'n';
175
+ if (typeof node.value === 'number') return node.raw ?? String(node.value);
176
+ if (typeof node.value === 'boolean') return String(node.value);
177
+ if (node.regex) return node.raw;
178
+ return String(node.value);
179
+ }
180
+
181
+ function printJSXElementName(node) {
182
+ if (!node) return '';
183
+ if (node.type === 'JSXIdentifier') return node.name;
184
+ if (node.type === 'JSXMemberExpression') return printJSXElementName(node.object) + '.' + printJSXElementName(node.property);
185
+ if (node.type === 'JSXNamespacedName') return node.namespace.name + ':' + node.name.name;
186
+ return '';
187
+ }
188
+
189
+ function normalizeJSXText(raw) {
190
+ return raw.replace(/\s+/g, ' ').trim();
191
+ }
192
+
193
+ function findForAnnotation(node, options) {
194
+ const anns = options.__vskAnnotations;
195
+ if (!anns || !Array.isArray(anns)) return null;
196
+ return anns.find((a) => a.kind === 'for-clause' && a.forStart === node.start) || null;
197
+ }
198
+
199
+ // ── JSX ─────────────────────────────────────────────────────────
200
+
201
+ function printJSXOpeningElement(node, path, options, print) {
202
+ const tagName = printJSXElementName(node.name);
203
+ const attrs = node.attributes || [];
204
+ const attrDocs = [];
205
+ for (let i = 0; i < attrs.length; i++) {
206
+ if (attrs[i].type === 'JSXSpreadAttribute') {
207
+ attrDocs.push(['{...', path.call(print, 'attributes', i, 'argument'), '}']);
208
+ } else {
209
+ attrDocs.push(path.call(print, 'attributes', i));
210
+ }
211
+ }
212
+ const hasAttrs = attrDocs.length > 0;
213
+ const typeArgs = node.typeArguments ? path.call(print, 'typeArguments') : '';
214
+ const attrsDoc = hasAttrs ? indent([line, join(line, attrDocs)]) : '';
215
+ if (node.selfClosing) {
216
+ return group(['<', tagName, typeArgs, attrsDoc, hasAttrs ? line : ' ', '/>']);
217
+ }
218
+ return group(['<', tagName, typeArgs, attrsDoc, hasAttrs ? softline : '', '>']);
219
+ }
220
+
221
+ const VESK_JSX_HEADER_RE = /^for\s*\([\s\S]*\)$/;
222
+ const VESK_JSX_KEYWORD_RE = /^(empty|else)$/;
223
+
224
+ function printJSXChildren(node, path, options, print) {
225
+ const children = node.children || [];
226
+ const docs = [];
227
+ const childNodes = [];
228
+ let pendingText = '';
229
+ let pendingTextNode = null;
230
+
231
+ const flush = () => {
232
+ if (pendingText) {
233
+ docs.push(pendingText);
234
+ childNodes.push(pendingTextNode);
235
+ pendingText = '';
236
+ pendingTextNode = null;
237
+ }
238
+ };
239
+
240
+ for (let i = 0; i < children.length; i++) {
241
+ const child = children[i];
242
+ if (child.type === 'JSXText') {
243
+ const raw = options.originalText.slice(child.start, child.end);
244
+ const text = normalizeJSXText(raw);
245
+ const next = children[i + 1];
246
+ const isControlHeader =
247
+ (VESK_JSX_HEADER_RE.test(text) || VESK_JSX_KEYWORD_RE.test(text)) &&
248
+ next &&
249
+ next.type === 'JSXExpressionContainer' &&
250
+ next.expression &&
251
+ next.expression.type !== 'JSXEmptyExpression';
252
+ if (isControlHeader) {
253
+ flush();
254
+ const exprDoc = path.call((p) => print(p), 'children', i + 1, 'expression');
255
+ docs.push([text, ' ', '{', indent([hardline, exprDoc]), hardline, '}']);
256
+ childNodes.push(next);
257
+ i++;
258
+ continue;
259
+ }
260
+ if (!text) continue;
261
+ if (pendingText) pendingText += ' ' + text;
262
+ else { pendingText = text; pendingTextNode = child; }
263
+ } else {
264
+ flush();
265
+ docs.push(path.call(print, 'children', i));
266
+ childNodes.push(child);
267
+ }
268
+ }
269
+ flush();
270
+
271
+ return { docs, childNodes };
272
+ }
273
+
274
+ function printJSXElement(node, path, options, print) {
275
+ const tagName = printJSXElementName(node.openingElement.name);
276
+ const openTag = path.call(
277
+ (openPath) => printJSXOpeningElement(openPath.node, openPath, options, print),
278
+ 'openingElement',
279
+ );
280
+
281
+ if (node.openingElement.selfClosing || !node.closingElement) {
282
+ return openTag;
283
+ }
284
+
285
+ if (tagName === 'style') {
286
+ // Preserve <style> content verbatim (CSS is not re-flowed).
287
+ const raw = options.originalText.slice(node.start, node.end);
288
+ return raw;
289
+ }
290
+
291
+ const { docs, childNodes } = printJSXChildren(node, path, options, print);
292
+ const closeTag = ['</', tagName, '>'];
293
+
294
+ if (docs.length === 0) {
295
+ return [openTag, closeTag];
296
+ }
297
+
298
+ // Single inline text child keeps the tightest layout.
299
+ if (docs.length === 1 && typeof docs[0] === 'string' && wasOriginallySingleLine(node)) {
300
+ return group([openTag, indent([softline, docs[0]]), softline, closeTag]);
301
+ }
302
+
303
+ // Single simple expression child hugs the tags: <h1>{count}</h1>
304
+ if (
305
+ docs.length === 1 &&
306
+ childNodes.length === 1 &&
307
+ childNodes[0].type === 'JSXExpressionContainer' &&
308
+ isSimpleExpression(childNodes[0].expression)
309
+ ) {
310
+ return group([openTag, docs[0], closeTag]);
311
+ }
312
+
313
+ const body = [];
314
+ for (let i = 0; i < docs.length; i++) {
315
+ body.push(typeof docs[i] === 'string' ? docs[i] : docs[i]);
316
+ if (i < docs.length - 1) {
317
+ body.push(hasBlankLineBetween(childNodes[i], childNodes[i + 1]) ? [hardline, hardline] : hardline);
318
+ }
319
+ }
320
+
321
+ return group([openTag, indent([hardline, ...body]), hardline, closeTag]);
322
+ }
323
+
324
+ function printJSXFragment(node, path, options, print) {
325
+ const { docs, childNodes } = printJSXChildren(node, path, options, print);
326
+ if (docs.length === 0) return '<></>';
327
+ if (docs.length === 1 && wasOriginallySingleLine(node)) {
328
+ return group(['<>', indent([softline, docs[0]]), softline, '</>']);
329
+ }
330
+ const body = [];
331
+ for (let i = 0; i < docs.length; i++) {
332
+ body.push(docs[i]);
333
+ if (i < docs.length - 1) {
334
+ body.push(hasBlankLineBetween(childNodes[i], childNodes[i + 1]) ? [hardline, hardline] : hardline);
335
+ }
336
+ }
337
+ return group(['<>', indent([hardline, ...body]), hardline, '</>']);
338
+ }
339
+
340
+ function isSimpleExpression(node) {
341
+ if (!node) return false;
342
+ switch (node.type) {
343
+ case 'Identifier':
344
+ case 'Literal':
345
+ case 'TemplateLiteral':
346
+ case 'MemberExpression':
347
+ case 'CallExpression':
348
+ case 'ThisExpression':
349
+ return true;
350
+ default:
351
+ return false;
352
+ }
353
+ }
354
+
355
+ // ── Expressions ─────────────────────────────────────────────────
356
+
357
+ function printBinaryExpression(node, path, options, print, parent) {
358
+ const leftDoc = path.call(print, 'left');
359
+ const rightDoc = path.call(print, 'right');
360
+ const left = node.left;
361
+ const right = node.right;
362
+
363
+ const wrapLeft = needsBinaryParens(left, node.operator, 'left') || isSequenceLike(left);
364
+ const wrapRight = needsBinaryParens(right, node.operator, 'right') || isSequenceLike(right);
365
+
366
+ const doc = [
367
+ wrapLeft ? ['(', leftDoc, ')'] : leftDoc,
368
+ ' ',
369
+ node.operator,
370
+ ' ',
371
+ wrapRight ? ['(', rightDoc, ')'] : rightDoc,
372
+ ];
373
+
374
+ if (parent && (parent.type === 'IfStatement' || parent.type === 'WhileStatement' || parent.type === 'ForStatement')) {
375
+ return doc;
376
+ }
377
+ return doc;
378
+ }
379
+
380
+ function isSequenceLike(node) {
381
+ return node && node.type === 'SequenceExpression';
382
+ }
383
+
384
+ // ── TypeScript nodes ────────────────────────────────────────────
385
+
386
+ function printTSType(node, path, options, print) {
387
+ const kind = {
388
+ TSStringKeyword: 'string',
389
+ TSNumberKeyword: 'number',
390
+ TSBooleanKeyword: 'boolean',
391
+ TSAnyKeyword: 'any',
392
+ TSUnknownKeyword: 'unknown',
393
+ TSVoidKeyword: 'void',
394
+ TSNeverKeyword: 'never',
395
+ TSObjectKeyword: 'object',
396
+ TSSymbolKeyword: 'symbol',
397
+ TSBigIntKeyword: 'bigint',
398
+ TSIntrinsicKeyword: 'intrinsic',
399
+ TSThisType: 'this',
400
+ TSUndefinedKeyword: 'undefined',
401
+ TSNullKeyword: 'null',
402
+ };
403
+ return kind[node.type];
404
+ }
405
+
406
+ // ── Main dispatch ───────────────────────────────────────────────
407
+
408
+ function finishVeskNode(node, parts, nodeContent) {
409
+ if (node.trailingComments && node.trailingComments.length > 0) {
410
+ const trailingParts = [];
411
+ let previousComment = null;
412
+ for (let i = 0; i < node.trailingComments.length; i++) {
413
+ const comment = node.trailingComments[i];
414
+ const isInline = node.loc && comment.loc && node.loc.end.line === comment.loc.start.line;
415
+ const commentDoc = printComment(comment);
416
+ if (isInline) {
417
+ if (comment.type === 'Line') {
418
+ trailingParts.push(lineSuffix([' ', commentDoc]));
419
+ trailingParts.push(breakParent);
420
+ } else {
421
+ trailingParts.push(' ' + commentDoc);
422
+ }
423
+ } else {
424
+ const refs = [hardline];
425
+ if (previousComment) {
426
+ if (hasBlankLineBetween(previousComment, comment)) refs.push(hardline);
427
+ } else if (hasBlankLineBetween(node, comment)) {
428
+ refs.push(hardline);
429
+ }
430
+ refs.push(commentDoc);
431
+ trailingParts.push(lineSuffix(refs));
432
+ }
433
+ previousComment = comment;
434
+ }
435
+ parts.push(nodeContent, ...trailingParts);
436
+ return parts;
437
+ }
438
+ if (parts.length > 0) {
439
+ parts.push(nodeContent);
440
+ return parts;
441
+ }
442
+ return nodeContent;
443
+ }
444
+
445
+ export function printVeskNode(path, options, print) {
446
+ const node = path.node;
447
+ if (!node || typeof node !== 'object') {
448
+ return String(node ?? '');
449
+ }
450
+
451
+ const parts = [];
452
+
453
+ // Leading comments
454
+ if (node.leadingComments && node.leadingComments.length > 0) {
455
+ for (let i = 0; i < node.leadingComments.length; i++) {
456
+ const comment = node.leadingComments[i];
457
+ parts.push(printComment(comment));
458
+ parts.push(hardline);
459
+ if (i < node.leadingComments.length - 1 && hasBlankLineBetween(comment, node.leadingComments[i + 1])) {
460
+ parts.push(hardline);
461
+ } else if (i === node.leadingComments.length - 1 && hasBlankLineBetween(comment, node)) {
462
+ parts.push(hardline);
463
+ }
464
+ }
465
+ }
466
+
467
+ // prettier-ignore: keep the node's original text verbatim.
468
+ if (node.leadingComments && node.leadingComments.some((c) => c.value.trim() === 'prettier-ignore')) {
469
+ const raw = options.originalText.slice(node.start, node.end);
470
+ return finishVeskNode(node, parts, replaceEndOfLine(raw));
471
+ }
472
+
473
+ let nodeContent;
474
+
475
+ switch (node.type) {
476
+ case 'Program': {
477
+ const stmts = [];
478
+ const bodyDocs = path.map(print, 'body');
479
+ for (let i = 0; i < bodyDocs.length; i++) {
480
+ stmts.push(bodyDocs[i]);
481
+ if (i < bodyDocs.length - 1) {
482
+ stmts.push(hasBlankLineBetween(node.body[i], node.body[i + 1]) ? [hardline, hardline] : hardline);
483
+ }
484
+ }
485
+ nodeContent = stmts.length > 0 ? [...stmts, hardline] : stmts;
486
+ break;
487
+ }
488
+
489
+ case 'ImportDeclaration': {
490
+ const specs = node.specifiers || [];
491
+ const source = printStringLiteral(node.source.value, options);
492
+ if (specs.length === 0) {
493
+ nodeContent = group(['import ', source, semi(options)]);
494
+ break;
495
+ }
496
+ const defaultSpec = specs.find((s) => s.type === 'ImportDefaultSpecifier');
497
+ const namespaceSpec = specs.find((s) => s.type === 'ImportNamespaceSpecifier');
498
+ const namedSpecs = specs.filter((s) => s.type === 'ImportSpecifier');
499
+ const leading = [];
500
+ if (defaultSpec) {
501
+ leading.push(path.call(print, 'specifiers', specs.indexOf(defaultSpec)));
502
+ }
503
+ if (namespaceSpec) {
504
+ leading.push(path.call(print, 'specifiers', specs.indexOf(namespaceSpec)));
505
+ }
506
+ const specDocs = [];
507
+ for (const s of namedSpecs) specDocs.push(path.call(print, 'specifiers', specs.indexOf(s)));
508
+ const header = ['import '];
509
+ if (leading.length) header.push(leading.join(', '));
510
+ if (leading.length && namedSpecs.length) header.push(', ');
511
+ if (namedSpecs.length) {
512
+ header.push(
513
+ group([
514
+ '{',
515
+ indent([softline, join([',', line], specDocs)]),
516
+ ifBreak(options.trailingComma === 'none' ? '' : ','),
517
+ softline,
518
+ '}',
519
+ ]),
520
+ );
521
+ }
522
+ header.push(' from ', source, semi(options));
523
+ nodeContent = group(header);
524
+ break;
525
+ }
526
+
527
+ case 'ImportSpecifier':
528
+ nodeContent = [
529
+ path.call(print, 'imported'),
530
+ node.imported.name !== node.local.name ? [' as ', path.call(print, 'local')] : '',
531
+ ];
532
+ break;
533
+ case 'ImportDefaultSpecifier':
534
+ case 'ImportNamespaceSpecifier':
535
+ nodeContent = path.call(print, 'local');
536
+ break;
537
+
538
+ case 'ExportNamedDeclaration': {
539
+ if (node.declaration) {
540
+ nodeContent = ['export ', path.call(print, 'declaration')];
541
+ break;
542
+ }
543
+ const specs = node.specifiers || [];
544
+ const source = node.source ? [' from ', printStringLiteral(node.source.value, options)] : '';
545
+ nodeContent = group([
546
+ 'export {',
547
+ indent([softline, join([',', line], path.map(print, 'specifiers'))]),
548
+ ifBreak(options.trailingComma === 'none' ? '' : ','),
549
+ softline,
550
+ '}',
551
+ source,
552
+ semi(options),
553
+ ]);
554
+ break;
555
+ }
556
+ case 'ExportSpecifier':
557
+ nodeContent = [
558
+ path.call(print, 'local'),
559
+ node.exported.name !== node.local.name ? [' as ', path.call(print, 'exported')] : '',
560
+ ];
561
+ break;
562
+ case 'ExportDefaultDeclaration':
563
+ nodeContent = ['export default ', path.call(print, 'declaration')];
564
+ break;
565
+
566
+ case 'ComponentDeclaration': {
567
+ const idDoc = path.call(print, 'id');
568
+ const typeParams = node.typeParameters ? path.call(print, 'typeParameters') : '';
569
+ const paramsDoc =
570
+ node.params && node.params.length > 0
571
+ ? group([
572
+ '(',
573
+ indent([softline, join([',', line], path.map(print, 'params'))]),
574
+ ifBreak(options.trailingComma === 'none' ? '' : ','),
575
+ softline,
576
+ ')',
577
+ ])
578
+ : '';
579
+ const bodyDoc = path.call(print, 'body');
580
+ nodeContent = group([
581
+ 'component',
582
+ node.async ? ' async' : '',
583
+ ' ',
584
+ idDoc,
585
+ typeParams,
586
+ paramsDoc,
587
+ node.client ? ' client' : '',
588
+ ' ',
589
+ bodyDoc,
590
+ ]);
591
+ break;
592
+ }
593
+
594
+ case 'VariableDeclaration': {
595
+ const declDocs = path.map(print, 'declarations');
596
+ if (declDocs.length === 1) {
597
+ nodeContent = group([node.kind, ' ', declDocs[0], semi(options)]);
598
+ } else {
599
+ nodeContent = group([
600
+ node.kind,
601
+ indent([line, join([',', line], declDocs)]),
602
+ semi(options),
603
+ ]);
604
+ }
605
+ break;
606
+ }
607
+ case 'VariableDeclarator': {
608
+ const idDoc = path.call(print, 'id');
609
+ nodeContent = node.init
610
+ ? group([idDoc, ' = ', path.call(print, 'init')])
611
+ : idDoc;
612
+ break;
613
+ }
614
+
615
+ case 'FunctionDeclaration': {
616
+ const idDoc = path.call(print, 'id');
617
+ const paramsDoc = printFunctionParams(node, path, options, print);
618
+ nodeContent = group([
619
+ node.async ? 'async ' : '',
620
+ 'function',
621
+ node.generator ? '*' : '',
622
+ ' ',
623
+ idDoc,
624
+ paramsDoc,
625
+ ' ',
626
+ path.call(print, 'body'),
627
+ ]);
628
+ break;
629
+ }
630
+ case 'FunctionExpression': {
631
+ const idDoc = node.id ? [' ', path.call(print, 'id')] : '';
632
+ const paramsDoc = printFunctionParams(node, path, options, print);
633
+ nodeContent = group([
634
+ node.async ? 'async ' : '',
635
+ 'function',
636
+ node.generator ? '*' : '',
637
+ idDoc,
638
+ paramsDoc,
639
+ ' ',
640
+ path.call(print, 'body'),
641
+ ]);
642
+ break;
643
+ }
644
+ case 'ArrowFunctionExpression': {
645
+ const emptyParams = !node.params || node.params.length === 0;
646
+ const paramsDoc = emptyParams
647
+ ? '()'
648
+ : printParams(node, path, options, print);
649
+ const isBlockBody = node.body.type === 'BlockStatement';
650
+ nodeContent = group([
651
+ node.async ? 'async ' : '',
652
+ paramsDoc,
653
+ ' =>',
654
+ isBlockBody ? ' ' : indent([line, path.call(print, 'body')]),
655
+ isBlockBody ? path.call(print, 'body') : '',
656
+ ]);
657
+ break;
658
+ }
659
+
660
+ case 'BlockStatement':
661
+ nodeContent = printBlock(node, path, options, print);
662
+ break;
663
+
664
+ case 'VeskBlock': {
665
+ const bodyDocs = path.map(print, 'body');
666
+ if (!node.body.length) {
667
+ nodeContent = [node.tag, ' {}'];
668
+ break;
669
+ }
670
+ nodeContent = group([
671
+ node.tag,
672
+ ' {',
673
+ indent([hardline, ...printStatementList(bodyDocs, node.body, options)]),
674
+ hardline,
675
+ '}',
676
+ ]);
677
+ break;
678
+ }
679
+
680
+ case 'ExpressionStatement':
681
+ nodeContent = group([path.call(print, 'expression'), semi(options)]);
682
+ break;
683
+
684
+ case 'IfStatement': {
685
+ const testDoc = path.call(print, 'test');
686
+ const consequentDoc = path.call(print, 'consequent');
687
+ const alternate = node.alternate;
688
+ const alternateDoc = alternate
689
+ ? alternate.type === 'IfStatement'
690
+ ? path.call(print, 'alternate')
691
+ : ['else ', path.call(print, 'alternate')]
692
+ : '';
693
+ nodeContent = group([
694
+ 'if (',
695
+ testDoc,
696
+ ') ',
697
+ consequentDoc,
698
+ alternate ? [' ', alternateDoc] : '',
699
+ ]);
700
+ break;
701
+ }
702
+
703
+ case 'ForStatement': {
704
+ const initDoc = node.init ? path.call(print, 'init') : '';
705
+ const testDoc = node.test ? path.call(print, 'test') : '';
706
+ const updateDoc = node.update ? path.call(print, 'update') : '';
707
+ nodeContent = group([
708
+ 'for (',
709
+ initDoc,
710
+ '; ',
711
+ testDoc,
712
+ '; ',
713
+ updateDoc,
714
+ ') ',
715
+ path.call(print, 'body'),
716
+ ]);
717
+ break;
718
+ }
719
+ case 'ForInStatement':
720
+ case 'ForOfStatement': {
721
+ const ann = findForAnnotation(node, options);
722
+ let extra = '';
723
+ if (ann) {
724
+ if (ann.keyRange) {
725
+ const keyText = options.originalText.slice(ann.keyRange[0], ann.keyRange[1]);
726
+ extra = ['; key ', keyText];
727
+ } else if (ann.indexName) {
728
+ extra = ['; index ', ann.indexName];
729
+ }
730
+ }
731
+ nodeContent = group([
732
+ 'for',
733
+ node.type === 'ForOfStatement' && node.await ? ' await' : '',
734
+ ' (',
735
+ path.call(print, 'left'),
736
+ ' ',
737
+ node.type === 'ForOfStatement' ? 'of' : 'in',
738
+ ' ',
739
+ path.call(print, 'right'),
740
+ extra,
741
+ ') ',
742
+ path.call(print, 'body'),
743
+ ]);
744
+ break;
745
+ }
746
+ case 'WhileStatement':
747
+ nodeContent = group(['while (', path.call(print, 'test'), ') ', path.call(print, 'body')]);
748
+ break;
749
+ case 'DoWhileStatement':
750
+ nodeContent = group(['do ', path.call(print, 'body'), ' while (', path.call(print, 'test'), ')', semi(options)]);
751
+ break;
752
+
753
+ case 'TryStatement': {
754
+ const parts2 = ['try ', path.call(print, 'block')];
755
+ if (node.handler) {
756
+ const paramDoc = node.handler.param
757
+ ? [' (', path.call(print, 'handler', 'param'), ')']
758
+ : '';
759
+ parts2.push(' catch', paramDoc, ' ', path.call(print, 'handler', 'body'));
760
+ }
761
+ if (node.finalizer) {
762
+ parts2.push(' finally ', path.call(print, 'finalizer'));
763
+ }
764
+ nodeContent = group(parts2);
765
+ break;
766
+ }
767
+ case 'ThrowStatement':
768
+ nodeContent = group(['throw ', path.call(print, 'argument'), semi(options)]);
769
+ break;
770
+ case 'ReturnStatement':
771
+ nodeContent = group([
772
+ 'return',
773
+ node.argument ? [' ', path.call(print, 'argument')] : '',
774
+ semi(options),
775
+ ]);
776
+ break;
777
+ case 'BreakStatement':
778
+ nodeContent = ['break', node.label ? [' ', path.call(print, 'label')] : '', semi(options)];
779
+ break;
780
+ case 'ContinueStatement':
781
+ nodeContent = ['continue', node.label ? [' ', path.call(print, 'label')] : '', semi(options)];
782
+ break;
783
+ case 'DebuggerStatement':
784
+ nodeContent = ['debugger', semi(options)];
785
+ break;
786
+ case 'EmptyStatement':
787
+ nodeContent = ';';
788
+ break;
789
+ case 'LabeledStatement':
790
+ nodeContent = group([path.call(print, 'label'), ': ', path.call(print, 'body')]);
791
+ break;
792
+
793
+ case 'SwitchStatement': {
794
+ const caseDocs = path.map(print, 'cases');
795
+ nodeContent = group([
796
+ 'switch (',
797
+ path.call(print, 'discriminant'),
798
+ ') {',
799
+ indent([hardline, join(hardline, caseDocs)]),
800
+ hardline,
801
+ '}',
802
+ ]);
803
+ break;
804
+ }
805
+ case 'SwitchCase': {
806
+ const testDoc = node.test
807
+ ? ['case ', path.call(print, 'test'), ':']
808
+ : 'default:';
809
+ if (!node.consequent.length) {
810
+ nodeContent = testDoc;
811
+ } else {
812
+ const bodyDocs = path.map(print, 'consequent');
813
+ nodeContent = [
814
+ testDoc,
815
+ indent([hardline, ...printStatementList(bodyDocs, node.consequent, options)]),
816
+ ];
817
+ }
818
+ break;
819
+ }
820
+
821
+ case 'ClassDeclaration': {
822
+ const extendsDoc = node.superClass ? [' extends ', path.call(print, 'superClass')] : '';
823
+ nodeContent = group([
824
+ 'class ',
825
+ path.call(print, 'id'),
826
+ extendsDoc,
827
+ ' ',
828
+ path.call(print, 'body'),
829
+ ]);
830
+ break;
831
+ }
832
+ case 'ClassBody': {
833
+ const memberDocs = path.map(print, 'body');
834
+ nodeContent = group([
835
+ '{',
836
+ indent([hardline, join(hardline, memberDocs)]),
837
+ hardline,
838
+ '}',
839
+ ]);
840
+ break;
841
+ }
842
+ case 'MethodDefinition': {
843
+ const keyDoc = path.call(print, 'key');
844
+ const paramsDoc = printParams(node.value, path, options, print);
845
+ const prefix =
846
+ (node.static ? 'static ' : '') +
847
+ (node.kind === 'get' ? 'get ' : node.kind === 'set' ? 'set ' : '');
848
+ nodeContent = group([prefix, keyDoc, paramsDoc, ' ', path.call(print, 'value', 'body')]);
849
+ break;
850
+ }
851
+ case 'PropertyDefinition': {
852
+ nodeContent = group([
853
+ node.static ? 'static ' : '',
854
+ path.call(print, 'key'),
855
+ node.value ? [' = ', path.call(print, 'value')] : '',
856
+ semi(options),
857
+ ]);
858
+ break;
859
+ }
860
+
861
+ case 'CallExpression': {
862
+ const calleeDoc = path.call(print, 'callee');
863
+ const argsDoc = printCallArguments(node.arguments, path, options, print);
864
+ nodeContent = group([calleeDoc, argsDoc]);
865
+ break;
866
+ }
867
+ case 'NewExpression': {
868
+ const argsDoc = printCallArguments(node.arguments, path, options, print);
869
+ nodeContent = group(['new ', path.call(print, 'callee'), argsDoc]);
870
+ break;
871
+ }
872
+ case 'MemberExpression': {
873
+ const objDoc = path.call(print, 'object');
874
+ const propDoc = path.call(print, 'property');
875
+ nodeContent = node.computed
876
+ ? [objDoc, node.optional ? '?.[' : '[', propDoc, ']']
877
+ : [objDoc, node.optional ? '?.' : '.', propDoc];
878
+ break;
879
+ }
880
+ case 'ChainExpression':
881
+ nodeContent = path.call(print, 'expression');
882
+ break;
883
+
884
+ case 'UnaryExpression': {
885
+ const arg = node.argument;
886
+ const wrap =
887
+ arg.type === 'BinaryExpression' ||
888
+ arg.type === 'UnaryExpression' ||
889
+ arg.type === 'UpdateExpression' ||
890
+ arg.type === 'SequenceExpression';
891
+ nodeContent = [
892
+ node.operator,
893
+ wrap ? ['(', path.call(print, 'argument'), ')'] : path.call(print, 'argument'),
894
+ ];
895
+ break;
896
+ }
897
+ case 'UpdateExpression':
898
+ nodeContent = node.prefix
899
+ ? [node.operator, path.call(print, 'argument')]
900
+ : [path.call(print, 'argument'), node.operator];
901
+ break;
902
+ case 'BinaryExpression':
903
+ nodeContent = printBinaryExpression(node, path, options, print);
904
+ break;
905
+ case 'LogicalExpression':
906
+ nodeContent = printBinaryExpression(node, path, options, print);
907
+ break;
908
+ case 'AssignmentExpression': {
909
+ const wrapLeft =
910
+ node.left.type === 'BinaryExpression' || node.left.type === 'SequenceExpression';
911
+ nodeContent = group([
912
+ wrapLeft ? ['(', path.call(print, 'left'), ')'] : path.call(print, 'left'),
913
+ ' ',
914
+ node.operator,
915
+ ' ',
916
+ node.right.type === 'AssignmentExpression' || node.right.type === 'SequenceExpression'
917
+ ? ['(', path.call(print, 'right'), ')']
918
+ : path.call(print, 'right'),
919
+ ]);
920
+ break;
921
+ }
922
+ case 'ConditionalExpression': {
923
+ const testDoc = path.call(print, 'test');
924
+ nodeContent = group([
925
+ testDoc.type === 'BinaryExpression' || testDoc.type === 'SequenceExpression'
926
+ ? ['(', testDoc, ')']
927
+ : testDoc,
928
+ indent([line, '? ', path.call(print, 'consequent')]),
929
+ indent([line, ': ', path.call(print, 'alternate')]),
930
+ ]);
931
+ break;
932
+ }
933
+ case 'SequenceExpression':
934
+ nodeContent = join(', ', path.map(print, 'expressions'));
935
+ break;
936
+
937
+ case 'ObjectExpression': {
938
+ if (!node.properties.length) {
939
+ nodeContent = '{}';
940
+ break;
941
+ }
942
+ const propDocs = path.map(print, 'properties');
943
+ nodeContent = group([
944
+ '{',
945
+ options.bracketSpacing ? ' ' : '',
946
+ indent([softline, join([',', line], propDocs)]),
947
+ ifBreak(options.trailingComma === 'none' ? '' : ','),
948
+ softline,
949
+ options.bracketSpacing ? ' ' : '',
950
+ '}',
951
+ ]);
952
+ break;
953
+ }
954
+ case 'Property': {
955
+ const keyDoc = path.call(print, 'key');
956
+ if (node.method) {
957
+ const paramsDoc = printParams(node.value, path, options, print);
958
+ nodeContent = group([keyDoc, paramsDoc, ' ', path.call(print, 'value', 'body')]);
959
+ break;
960
+ }
961
+ if (node.kind === 'get' || node.kind === 'set') {
962
+ const paramsDoc = printParams(node.value, path, options, print);
963
+ nodeContent = group([node.kind, ' ', keyDoc, paramsDoc, ' ', path.call(print, 'value', 'body')]);
964
+ break;
965
+ }
966
+ if (node.shorthand) {
967
+ nodeContent = keyDoc;
968
+ break;
969
+ }
970
+ const valueDoc = path.call(print, 'value');
971
+ nodeContent = group([
972
+ node.computed ? '[' : '',
973
+ keyDoc,
974
+ node.computed ? ']' : '',
975
+ ': ',
976
+ valueDoc,
977
+ ]);
978
+ break;
979
+ }
980
+ case 'SpreadElement':
981
+ nodeContent = ['...', path.call(print, 'argument')];
982
+ break;
983
+
984
+ case 'ArrayExpression': {
985
+ if (!node.elements.length) {
986
+ nodeContent = '[]';
987
+ break;
988
+ }
989
+ const elemDocs = path.map(print, 'elements');
990
+ nodeContent = group([
991
+ '[',
992
+ indent([softline, join([',', line], elemDocs)]),
993
+ ifBreak(options.trailingComma === 'none' ? '' : ','),
994
+ softline,
995
+ ']',
996
+ ]);
997
+ break;
998
+ }
999
+
1000
+ case 'TemplateLiteral': {
1001
+ const parts2 = ['`'];
1002
+ for (let i = 0; i < node.quasis.length; i++) {
1003
+ parts2.push(node.quasis[i].value.raw);
1004
+ if (i < node.expressions.length) {
1005
+ parts2.push('${', path.call(print, 'expressions', i), '}');
1006
+ }
1007
+ }
1008
+ parts2.push('`');
1009
+ nodeContent = parts2;
1010
+ break;
1011
+ }
1012
+ case 'TemplateElement':
1013
+ nodeContent = node.value.raw;
1014
+ break;
1015
+ case 'TaggedTemplateExpression':
1016
+ nodeContent = group([path.call(print, 'tag'), path.call(print, 'quasi')]);
1017
+ break;
1018
+
1019
+ case 'Identifier':
1020
+ nodeContent = node.name;
1021
+ break;
1022
+ case 'PrivateIdentifier':
1023
+ nodeContent = '#' + node.name;
1024
+ break;
1025
+ case 'Literal':
1026
+ nodeContent = printLiteral(node, options);
1027
+ break;
1028
+ case 'ThisExpression':
1029
+ nodeContent = 'this';
1030
+ break;
1031
+ case 'Super':
1032
+ nodeContent = 'super';
1033
+ break;
1034
+ case 'AwaitExpression':
1035
+ nodeContent = ['await ', path.call(print, 'argument')];
1036
+ break;
1037
+ case 'YieldExpression':
1038
+ nodeContent = [
1039
+ 'yield',
1040
+ node.delegate ? '*' : '',
1041
+ node.argument ? [' ', path.call(print, 'argument')] : '',
1042
+ ];
1043
+ break;
1044
+ case 'MetaProperty':
1045
+ nodeContent = [path.call(print, 'meta'), '.', path.call(print, 'property')];
1046
+ break;
1047
+ case 'ImportExpression':
1048
+ nodeContent = group(['import(', path.call(print, 'source'), ')']);
1049
+ break;
1050
+
1051
+ // Patterns
1052
+ case 'ArrayPattern': {
1053
+ const elemDocs = path.map(print, 'elements');
1054
+ nodeContent = group([
1055
+ node.lazy ? '&[' : '[',
1056
+ indent([softline, join([',', line], elemDocs)]),
1057
+ ifBreak(options.trailingComma === 'none' ? '' : ','),
1058
+ softline,
1059
+ ']',
1060
+ ]);
1061
+ break;
1062
+ }
1063
+ case 'ObjectPattern': {
1064
+ if (!node.properties.length) {
1065
+ nodeContent = '{}';
1066
+ break;
1067
+ }
1068
+ const propDocs = path.map(print, 'properties');
1069
+ nodeContent = group([
1070
+ '{',
1071
+ indent([softline, join([',', line], propDocs)]),
1072
+ ifBreak(options.trailingComma === 'none' ? '' : ','),
1073
+ softline,
1074
+ '}',
1075
+ ]);
1076
+ break;
1077
+ }
1078
+ case 'AssignmentPattern':
1079
+ nodeContent = group([
1080
+ path.call(print, 'left'),
1081
+ ' = ',
1082
+ path.call(print, 'right'),
1083
+ ]);
1084
+ break;
1085
+ case 'RestElement':
1086
+ nodeContent = ['...', path.call(print, 'argument')];
1087
+ break;
1088
+
1089
+ // TypeScript
1090
+ case 'TSTypeAnnotation':
1091
+ nodeContent = [': ', path.call(print, 'typeAnnotation')];
1092
+ break;
1093
+ case 'TSTypeReference': {
1094
+ const nameDoc = path.call(print, 'typeName');
1095
+ nodeContent = [
1096
+ nameDoc,
1097
+ node.typeArguments ? path.call(print, 'typeArguments') : node.typeParameters ? path.call(print, 'typeParameters') : '',
1098
+ ];
1099
+ break;
1100
+ }
1101
+ case 'TSQualifiedName':
1102
+ nodeContent = [path.call(print, 'left'), '.', path.call(print, 'right')];
1103
+ break;
1104
+ case 'TSArrayType':
1105
+ nodeContent = [path.call(print, 'elementType'), '[]'];
1106
+ break;
1107
+ case 'TSUnionType':
1108
+ nodeContent = join(' | ', path.map(print, 'types'));
1109
+ break;
1110
+ case 'TSIntersectionType':
1111
+ nodeContent = join(' & ', path.map(print, 'types'));
1112
+ break;
1113
+ case 'TSTupleType':
1114
+ nodeContent = group([
1115
+ '[',
1116
+ indent([softline, join([',', line], path.map(print, 'elementTypes'))]),
1117
+ ifBreak(options.trailingComma === 'none' ? '' : ','),
1118
+ softline,
1119
+ ']',
1120
+ ]);
1121
+ break;
1122
+ case 'TSNamedTupleMember':
1123
+ nodeContent = group([path.call(print, 'label'), ': ', path.call(print, 'elementType')]);
1124
+ break;
1125
+ case 'TSOptionalType':
1126
+ nodeContent = [path.call(print, 'typeAnnotation'), '?'];
1127
+ break;
1128
+ case 'TSLiteralType':
1129
+ nodeContent = path.call(print, 'literal');
1130
+ break;
1131
+ case 'TSFunctionType': {
1132
+ const paramsDoc = printParams(node, path, options, print);
1133
+ nodeContent = group([paramsDoc, ' => ', path.call(print, 'returnType')]);
1134
+ break;
1135
+ }
1136
+ case 'TSConstructorType':
1137
+ nodeContent = group(['new ', printParams(node, path, options, print), ' => ', path.call(print, 'returnType')]);
1138
+ break;
1139
+ case 'TSTypeLiteral': {
1140
+ if (!node.members.length) {
1141
+ nodeContent = '{}';
1142
+ break;
1143
+ }
1144
+ const memberDocs = path.map(print, 'members');
1145
+ nodeContent = group([
1146
+ '{',
1147
+ indent([hardline, ...printStatementList(memberDocs, node.members, options)]),
1148
+ hardline,
1149
+ '}',
1150
+ ]);
1151
+ break;
1152
+ }
1153
+ case 'TSPropertySignature': {
1154
+ const optional = node.optional ? '?' : '';
1155
+ nodeContent = group([
1156
+ node.computed ? '[' : '',
1157
+ path.call(print, 'key'),
1158
+ node.computed ? ']' : '',
1159
+ optional,
1160
+ node.typeAnnotation ? path.call(print, 'typeAnnotation') : '',
1161
+ semi(options),
1162
+ ]);
1163
+ break;
1164
+ }
1165
+ case 'TSMethodSignature': {
1166
+ const optional = node.optional ? '?' : '';
1167
+ const paramsDoc = printParams(node, path, options, print);
1168
+ nodeContent = group([
1169
+ path.call(print, 'key'),
1170
+ optional,
1171
+ paramsDoc,
1172
+ node.returnType ? path.call(print, 'returnType') : '',
1173
+ semi(options),
1174
+ ]);
1175
+ break;
1176
+ }
1177
+ case 'TSIndexSignature':
1178
+ nodeContent = group([
1179
+ '[',
1180
+ join(', ', path.map(print, 'parameters')),
1181
+ ']',
1182
+ node.typeAnnotation ? path.call(print, 'typeAnnotation') : '',
1183
+ semi(options),
1184
+ ]);
1185
+ break;
1186
+ case 'TSInterfaceDeclaration': {
1187
+ const extendsDoc = node.extends && node.extends.length
1188
+ ? [' extends ', join(', ', path.map(print, 'extends'))]
1189
+ : '';
1190
+ nodeContent = group([
1191
+ 'interface ',
1192
+ path.call(print, 'id'),
1193
+ extendsDoc,
1194
+ ' ',
1195
+ path.call(print, 'body'),
1196
+ ]);
1197
+ break;
1198
+ }
1199
+ case 'TSInterfaceBody': {
1200
+ const memberDocs = path.map(print, 'body');
1201
+ nodeContent = group([
1202
+ '{',
1203
+ indent([hardline, ...printStatementList(memberDocs, node.body, options)]),
1204
+ hardline,
1205
+ '}',
1206
+ ]);
1207
+ break;
1208
+ }
1209
+ case 'TSInterfaceHeritage':
1210
+ nodeContent = group([
1211
+ path.call(print, 'expression'),
1212
+ node.typeArguments ? path.call(print, 'typeArguments') : '',
1213
+ ]);
1214
+ break;
1215
+ case 'TSTypeAliasDeclaration':
1216
+ nodeContent = group([
1217
+ 'type ',
1218
+ path.call(print, 'id'),
1219
+ node.typeParameters ? path.call(print, 'typeParameters') : '',
1220
+ ' = ',
1221
+ path.call(print, 'typeAnnotation'),
1222
+ semi(options),
1223
+ ]);
1224
+ break;
1225
+ case 'TSEnumDeclaration': {
1226
+ const memberDocs = path.map(print, 'members');
1227
+ nodeContent = group([
1228
+ 'enum ',
1229
+ path.call(print, 'id'),
1230
+ ' {',
1231
+ indent([hardline, join([',', line], memberDocs)]),
1232
+ hardline,
1233
+ '}',
1234
+ ]);
1235
+ break;
1236
+ }
1237
+ case 'TSEnumMember':
1238
+ nodeContent = group([
1239
+ path.call(print, 'id'),
1240
+ node.initializer ? [' = ', path.call(print, 'initializer')] : '',
1241
+ ]);
1242
+ break;
1243
+ case 'TSTypeParameterDeclaration':
1244
+ nodeContent = group([
1245
+ '<',
1246
+ indent([softline, join([',', line], path.map(print, 'params'))]),
1247
+ ifBreak(options.trailingComma === 'none' ? '' : ','),
1248
+ softline,
1249
+ '>',
1250
+ ]);
1251
+ break;
1252
+ case 'TSTypeParameterInstantiation':
1253
+ nodeContent = group([
1254
+ '<',
1255
+ indent([softline, join([',', line], path.map(print, 'params'))]),
1256
+ ifBreak(options.trailingComma === 'none' ? '' : ','),
1257
+ softline,
1258
+ '>',
1259
+ ]);
1260
+ break;
1261
+ case 'TSTypeParameter': {
1262
+ const constraint = node.constraint ? [' extends ', path.call(print, 'constraint')] : '';
1263
+ const defaultVal = node.default ? [' = ', path.call(print, 'default')] : '';
1264
+ nodeContent = group([path.call(print, 'name'), constraint, defaultVal]);
1265
+ break;
1266
+ }
1267
+ case 'TSAsExpression':
1268
+ nodeContent = group([path.call(print, 'expression'), ' as ', path.call(print, 'typeAnnotation')]);
1269
+ break;
1270
+ case 'TSSatisfiesExpression':
1271
+ nodeContent = group([path.call(print, 'expression'), ' satisfies ', path.call(print, 'typeAnnotation')]);
1272
+ break;
1273
+ case 'TSIndexedAccessType':
1274
+ nodeContent = [path.call(print, 'objectType'), '[', path.call(print, 'indexType'), ']'];
1275
+ break;
1276
+ case 'TSConditionalType':
1277
+ nodeContent = group([
1278
+ path.call(print, 'checkType'),
1279
+ ' extends ',
1280
+ path.call(print, 'extendsType'),
1281
+ ' ? ',
1282
+ path.call(print, 'trueType'),
1283
+ ' : ',
1284
+ path.call(print, 'falseType'),
1285
+ ]);
1286
+ break;
1287
+ case 'TSMappedType':
1288
+ nodeContent = group(['{ [', path.call(print, 'key'), ']: ', path.call(print, 'typeAnnotation'), ' }']);
1289
+ break;
1290
+ case 'TSNonNullExpression':
1291
+ nodeContent = [path.call(print, 'expression'), '!'];
1292
+ break;
1293
+ case 'TSTypeOperator':
1294
+ nodeContent = [node.operator, ' ', path.call(print, 'typeAnnotation')];
1295
+ break;
1296
+
1297
+ // JSX
1298
+ case 'JSXElement':
1299
+ nodeContent = printJSXElement(node, path, options, print);
1300
+ break;
1301
+ case 'JSXFragment':
1302
+ nodeContent = printJSXFragment(node, path, options, print);
1303
+ break;
1304
+ case 'JSXOpeningElement':
1305
+ nodeContent = printJSXOpeningElement(node, path, options, print);
1306
+ break;
1307
+ case 'JSXClosingElement':
1308
+ nodeContent = ['</', printJSXElementName(node.name), '>'];
1309
+ break;
1310
+ case 'JSXAttribute': {
1311
+ const name = printJSXElementName(node.name);
1312
+ if (node.shorthand) {
1313
+ nodeContent = ['{', name, '}'];
1314
+ break;
1315
+ }
1316
+ if (!node.value) {
1317
+ nodeContent = name;
1318
+ break;
1319
+ }
1320
+ if (node.value.type === 'Literal') {
1321
+ nodeContent = [name, '=', quoteString(String(node.value.value), options.jsxSingleQuote)];
1322
+ break;
1323
+ }
1324
+ if (node.value.type === 'JSXExpressionContainer') {
1325
+ const expr = node.value.expression;
1326
+ if (expr.type === 'Literal' && typeof expr.value === 'string') {
1327
+ nodeContent = [name, '=', quoteString(expr.value, options.jsxSingleQuote)];
1328
+ break;
1329
+ }
1330
+ nodeContent = [name, '={', path.call(print, 'value', 'expression'), '}'];
1331
+ break;
1332
+ }
1333
+ nodeContent = name;
1334
+ break;
1335
+ }
1336
+ case 'JSXSpreadAttribute':
1337
+ nodeContent = ['{...', path.call(print, 'argument'), '}'];
1338
+ break;
1339
+ case 'JSXExpressionContainer': {
1340
+ if (node.expression.type === 'JSXEmptyExpression') {
1341
+ nodeContent = options.originalText.slice(node.start, node.end);
1342
+ break;
1343
+ }
1344
+ const exprDoc = path.call(print, 'expression');
1345
+ nodeContent = group(['{', exprDoc, '}']);
1346
+ break;
1347
+ }
1348
+ case 'JSXEmptyExpression':
1349
+ nodeContent = '';
1350
+ break;
1351
+ case 'JSXIdentifier':
1352
+ case 'JSXNamespacedName':
1353
+ case 'JSXMemberExpression':
1354
+ nodeContent = printJSXElementName(node);
1355
+ break;
1356
+ case 'JSXText':
1357
+ nodeContent = normalizeJSXText(node.value);
1358
+ break;
1359
+
1360
+ default: {
1361
+ // Known type-keyword nodes and catch-alls.
1362
+ const keyword = printTSType(node, path, options, print);
1363
+ if (keyword) {
1364
+ nodeContent = keyword;
1365
+ break;
1366
+ }
1367
+ nodeContent = '<<UNKNOWN:'+node.type+'>>';
1368
+ break;
1369
+ }
1370
+ }
1371
+
1372
+ // Inner comments (comments placed inside a node with no statement slot)
1373
+ if (node.innerComments && node.innerComments.length > 0) {
1374
+ const inner = node.innerComments.map(printComment);
1375
+ if (Array.isArray(nodeContent)) {
1376
+ return [...parts, ...inner, ...nodeContent];
1377
+ }
1378
+ return [...parts, ...inner, nodeContent];
1379
+ }
1380
+
1381
+ return finishVeskNode(node, parts, nodeContent);
1382
+ }