Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
81169 views
1
var assert = require("assert");
2
var sourceMap = require("source-map");
3
var printComments = require("./comments").printComments;
4
var linesModule = require("./lines");
5
var fromString = linesModule.fromString;
6
var concat = linesModule.concat;
7
var normalizeOptions = require("./options").normalize;
8
var getReprinter = require("./patcher").getReprinter;
9
var types = require("./types");
10
var namedTypes = types.namedTypes;
11
var isString = types.builtInTypes.string;
12
var isObject = types.builtInTypes.object;
13
var FastPath = require("./fast-path");
14
var util = require("./util");
15
16
function PrintResult(code, sourceMap) {
17
assert.ok(this instanceof PrintResult);
18
19
isString.assert(code);
20
this.code = code;
21
22
if (sourceMap) {
23
isObject.assert(sourceMap);
24
this.map = sourceMap;
25
}
26
}
27
28
var PRp = PrintResult.prototype;
29
var warnedAboutToString = false;
30
31
PRp.toString = function() {
32
if (!warnedAboutToString) {
33
console.warn(
34
"Deprecation warning: recast.print now returns an object with " +
35
"a .code property. You appear to be treating the object as a " +
36
"string, which might still work but is strongly discouraged."
37
);
38
39
warnedAboutToString = true;
40
}
41
42
return this.code;
43
};
44
45
var emptyPrintResult = new PrintResult("");
46
47
function Printer(originalOptions) {
48
assert.ok(this instanceof Printer);
49
50
var explicitTabWidth = originalOptions && originalOptions.tabWidth;
51
var options = normalizeOptions(originalOptions);
52
assert.notStrictEqual(options, originalOptions);
53
54
// It's common for client code to pass the same options into both
55
// recast.parse and recast.print, but the Printer doesn't need (and
56
// can be confused by) options.sourceFileName, so we null it out.
57
options.sourceFileName = null;
58
59
function printWithComments(path) {
60
assert.ok(path instanceof FastPath);
61
return printComments(path.getNode().comments, print(path), options);
62
}
63
64
function print(path, includeComments) {
65
if (includeComments)
66
return printWithComments(path);
67
68
assert.ok(path instanceof FastPath);
69
70
if (!explicitTabWidth) {
71
var oldTabWidth = options.tabWidth;
72
var loc = path.getNode().loc;
73
if (loc && loc.lines && loc.lines.guessTabWidth) {
74
options.tabWidth = loc.lines.guessTabWidth();
75
var lines = maybeReprint(path);
76
options.tabWidth = oldTabWidth;
77
return lines;
78
}
79
}
80
81
return maybeReprint(path);
82
}
83
84
function maybeReprint(path) {
85
var reprinter = getReprinter(path);
86
if (reprinter)
87
return maybeAddParens(path, reprinter(maybeReprint));
88
return printRootGenerically(path);
89
}
90
91
// Print the root node generically, but then resume reprinting its
92
// children non-generically.
93
function printRootGenerically(path) {
94
return genericPrint(path, options, printWithComments);
95
}
96
97
// Print the entire AST generically.
98
function printGenerically(path) {
99
return genericPrint(path, options, printGenerically);
100
}
101
102
this.print = function(ast) {
103
if (!ast) {
104
return emptyPrintResult;
105
}
106
107
var lines = print(FastPath.from(ast), true);
108
109
return new PrintResult(
110
lines.toString(options),
111
util.composeSourceMaps(
112
options.inputSourceMap,
113
lines.getSourceMap(
114
options.sourceMapName,
115
options.sourceRoot
116
)
117
)
118
);
119
};
120
121
this.printGenerically = function(ast) {
122
if (!ast) {
123
return emptyPrintResult;
124
}
125
126
var path = FastPath.from(ast);
127
var oldReuseWhitespace = options.reuseWhitespace;
128
129
// Do not reuse whitespace (or anything else, for that matter)
130
// when printing generically.
131
options.reuseWhitespace = false;
132
var pr = new PrintResult(printGenerically(path).toString(options));
133
options.reuseWhitespace = oldReuseWhitespace;
134
return pr;
135
};
136
}
137
138
exports.Printer = Printer;
139
140
function maybeAddParens(path, lines) {
141
return path.needsParens() ? concat(["(", lines, ")"]) : lines;
142
}
143
144
function genericPrint(path, options, printPath) {
145
assert.ok(path instanceof FastPath);
146
return maybeAddParens(path, genericPrintNoParens(path, options, printPath));
147
}
148
149
function genericPrintNoParens(path, options, print) {
150
var n = path.getValue();
151
152
if (!n) {
153
return fromString("");
154
}
155
156
if (typeof n === "string") {
157
return fromString(n, options);
158
}
159
160
namedTypes.Node.assert(n);
161
162
switch (n.type) {
163
case "File":
164
return path.call(print, "program");
165
166
case "Program":
167
return maybeAddSemicolon(path.call(function(bodyPath) {
168
return printStatementSequence(bodyPath, options, print);
169
}, "body"));
170
171
case "EmptyStatement":
172
return fromString("");
173
174
case "ExpressionStatement":
175
return concat([path.call(print, "expression"), ";"]);
176
177
case "BinaryExpression":
178
case "LogicalExpression":
179
case "AssignmentExpression":
180
return fromString(" ").join([
181
path.call(print, "left"),
182
n.operator,
183
path.call(print, "right")
184
]);
185
186
case "MemberExpression":
187
var parts = [path.call(print, "object")];
188
189
if (n.computed)
190
parts.push("[", path.call(print, "property"), "]");
191
else
192
parts.push(".", path.call(print, "property"));
193
194
return concat(parts);
195
196
case "Path":
197
return fromString(".").join(n.body);
198
199
case "Identifier":
200
return fromString(n.name, options);
201
202
case "SpreadElement":
203
case "SpreadElementPattern":
204
case "SpreadProperty":
205
case "SpreadPropertyPattern":
206
return concat(["...", path.call(print, "argument")]);
207
208
case "FunctionDeclaration":
209
case "FunctionExpression":
210
var parts = [];
211
212
if (n.async)
213
parts.push("async ");
214
215
parts.push("function");
216
217
if (n.generator)
218
parts.push("*");
219
220
if (n.id)
221
parts.push(" ", path.call(print, "id"));
222
223
parts.push(
224
"(",
225
printFunctionParams(path, options, print),
226
") ",
227
path.call(print, "body")
228
);
229
230
return concat(parts);
231
232
case "ArrowFunctionExpression":
233
var parts = [];
234
235
if (n.async)
236
parts.push("async ");
237
238
if (n.params.length === 1) {
239
parts.push(path.call(print, "params", 0));
240
} else {
241
parts.push(
242
"(",
243
printFunctionParams(path, options, print),
244
")"
245
);
246
}
247
248
parts.push(" => ", path.call(print, "body"));
249
250
return concat(parts);
251
252
case "MethodDefinition":
253
var parts = [];
254
255
if (n.static) {
256
parts.push("static ");
257
}
258
259
parts.push(printMethod(path, options, print));
260
261
return concat(parts);
262
263
case "YieldExpression":
264
var parts = ["yield"];
265
266
if (n.delegate)
267
parts.push("*");
268
269
if (n.argument)
270
parts.push(" ", path.call(print, "argument"));
271
272
return concat(parts);
273
274
case "AwaitExpression":
275
var parts = ["await"];
276
277
if (n.all)
278
parts.push("*");
279
280
if (n.argument)
281
parts.push(" ", path.call(print, "argument"));
282
283
return concat(parts);
284
285
case "ModuleDeclaration":
286
var parts = ["module", path.call(print, "id")];
287
288
if (n.source) {
289
assert.ok(!n.body);
290
parts.push("from", path.call(print, "source"));
291
} else {
292
parts.push(path.call(print, "body"));
293
}
294
295
return fromString(" ").join(parts);
296
297
case "ImportSpecifier":
298
case "ExportSpecifier":
299
var parts = [path.call(print, "id")];
300
301
if (n.name)
302
parts.push(" as ", path.call(print, "name"));
303
304
return concat(parts);
305
306
case "ExportBatchSpecifier":
307
return fromString("*");
308
309
case "ImportNamespaceSpecifier":
310
return concat(["* as ", path.call(print, "id")]);
311
312
case "ImportDefaultSpecifier":
313
return path.call(print, "id");
314
315
case "ExportDeclaration":
316
var parts = ["export"];
317
318
if (n["default"]) {
319
parts.push(" default");
320
321
} else if (n.specifiers &&
322
n.specifiers.length > 0) {
323
324
if (n.specifiers.length === 1 &&
325
n.specifiers[0].type === "ExportBatchSpecifier") {
326
parts.push(" *");
327
} else {
328
parts.push(
329
" { ",
330
fromString(", ").join(path.map(print, "specifiers")),
331
" }"
332
);
333
}
334
335
if (n.source)
336
parts.push(" from ", path.call(print, "source"));
337
338
parts.push(";");
339
340
return concat(parts);
341
}
342
343
if (n.declaration) {
344
if (!namedTypes.Node.check(n.declaration)) {
345
console.log(JSON.stringify(n, null, 2));
346
}
347
var decLines = path.call(print, "declaration");
348
parts.push(" ", decLines);
349
if (lastNonSpaceCharacter(decLines) !== ";") {
350
parts.push(";");
351
}
352
}
353
354
return concat(parts);
355
356
case "ImportDeclaration":
357
var parts = ["import "];
358
359
if (n.specifiers &&
360
n.specifiers.length > 0) {
361
362
var foundImportSpecifier = false;
363
364
path.each(function(specifierPath) {
365
var i = specifierPath.getName();
366
if (i > 0) {
367
parts.push(", ");
368
}
369
370
var value = specifierPath.getValue();
371
372
if (namedTypes.ImportDefaultSpecifier.check(value) ||
373
namedTypes.ImportNamespaceSpecifier.check(value)) {
374
assert.strictEqual(foundImportSpecifier, false);
375
} else {
376
namedTypes.ImportSpecifier.assert(value);
377
if (!foundImportSpecifier) {
378
foundImportSpecifier = true;
379
parts.push("{");
380
}
381
}
382
383
parts.push(print(specifierPath));
384
}, "specifiers");
385
386
if (foundImportSpecifier) {
387
parts.push("}");
388
}
389
390
parts.push(" from ");
391
}
392
393
parts.push(path.call(print, "source"), ";");
394
395
return concat(parts);
396
397
case "BlockStatement":
398
var naked = path.call(function(bodyPath) {
399
return printStatementSequence(bodyPath, options, print);
400
}, "body");
401
402
if (naked.isEmpty()) {
403
return fromString("{}");
404
}
405
406
return concat([
407
"{\n",
408
naked.indent(options.tabWidth),
409
"\n}"
410
]);
411
412
case "ReturnStatement":
413
var parts = ["return"];
414
415
if (n.argument) {
416
var argLines = path.call(print, "argument");
417
if (argLines.length > 1 &&
418
namedTypes.XJSElement &&
419
namedTypes.XJSElement.check(n.argument)) {
420
parts.push(
421
" (\n",
422
argLines.indent(options.tabWidth),
423
"\n)"
424
);
425
} else {
426
parts.push(" ", argLines);
427
}
428
}
429
430
parts.push(";");
431
432
return concat(parts);
433
434
case "CallExpression":
435
return concat([
436
path.call(print, "callee"),
437
printArgumentsList(path, options, print)
438
]);
439
440
case "ObjectExpression":
441
case "ObjectPattern":
442
var allowBreak = false,
443
len = n.properties.length,
444
parts = [len > 0 ? "{\n" : "{"];
445
446
path.map(function(childPath) {
447
var i = childPath.getName();
448
var prop = childPath.getValue();
449
var lines = print(childPath).indent(options.tabWidth);
450
451
var multiLine = lines.length > 1;
452
if (multiLine && allowBreak) {
453
// Similar to the logic for BlockStatement.
454
parts.push("\n");
455
}
456
457
parts.push(lines);
458
459
if (i < len - 1) {
460
// Add an extra line break if the previous object property
461
// had a multi-line value.
462
parts.push(multiLine ? ",\n\n" : ",\n");
463
allowBreak = !multiLine;
464
}
465
}, "properties");
466
467
parts.push(len > 0 ? "\n}" : "}");
468
469
return concat(parts);
470
471
case "PropertyPattern":
472
return concat([
473
path.call(print, "key"),
474
": ",
475
path.call(print, "pattern")
476
]);
477
478
case "Property": // Non-standard AST node type.
479
if (n.method || n.kind === "get" || n.kind === "set") {
480
return printMethod(path, options, print);
481
}
482
483
if (n.shorthand) {
484
return path.call(print, "key");
485
}
486
487
return concat([
488
path.call(print, "key"),
489
": ",
490
path.call(print, "value")
491
]);
492
493
case "ArrayExpression":
494
case "ArrayPattern":
495
var elems = n.elements,
496
len = elems.length,
497
parts = ["["];
498
499
path.each(function(elemPath) {
500
var i = elemPath.getName();
501
var elem = elemPath.getValue();
502
if (!elem) {
503
// If the array expression ends with a hole, that hole
504
// will be ignored by the interpreter, but if it ends with
505
// two (or more) holes, we need to write out two (or more)
506
// commas so that the resulting code is interpreted with
507
// both (all) of the holes.
508
parts.push(",");
509
} else {
510
if (i > 0)
511
parts.push(" ");
512
parts.push(print(elemPath));
513
if (i < len - 1)
514
parts.push(",");
515
}
516
}, "elements");
517
518
parts.push("]");
519
520
return concat(parts);
521
522
case "SequenceExpression":
523
return fromString(", ").join(path.map(print, "expressions"));
524
525
case "ThisExpression":
526
return fromString("this");
527
528
case "Literal":
529
if (typeof n.value !== "string")
530
return fromString(n.value, options);
531
532
// intentionally fall through...
533
534
case "ModuleSpecifier":
535
// A ModuleSpecifier is a string-valued Literal.
536
return fromString(nodeStr(n, options), options);
537
538
case "UnaryExpression":
539
var parts = [n.operator];
540
if (/[a-z]$/.test(n.operator))
541
parts.push(" ");
542
parts.push(path.call(print, "argument"));
543
return concat(parts);
544
545
case "UpdateExpression":
546
var parts = [path.call(print, "argument"), n.operator];
547
548
if (n.prefix)
549
parts.reverse();
550
551
return concat(parts);
552
553
case "ConditionalExpression":
554
return concat([
555
"(", path.call(print, "test"),
556
" ? ", path.call(print, "consequent"),
557
" : ", path.call(print, "alternate"), ")"
558
]);
559
560
case "NewExpression":
561
var parts = ["new ", path.call(print, "callee")];
562
var args = n.arguments;
563
if (args) {
564
parts.push(printArgumentsList(path, options, print));
565
}
566
567
return concat(parts);
568
569
case "VariableDeclaration":
570
var parts = [n.kind, " "];
571
var maxLen = 0;
572
var printed = path.map(function(childPath) {
573
var lines = print(childPath);
574
maxLen = Math.max(lines.length, maxLen);
575
return lines;
576
}, "declarations");
577
578
if (maxLen === 1) {
579
parts.push(fromString(", ").join(printed));
580
} else if (printed.length > 1 ) {
581
parts.push(
582
fromString(",\n").join(printed)
583
.indentTail(n.kind.length + 1)
584
);
585
} else {
586
parts.push(printed[0]);
587
}
588
589
// We generally want to terminate all variable declarations with a
590
// semicolon, except when they are children of for loops.
591
var parentNode = path.getParentNode();
592
if (!namedTypes.ForStatement.check(parentNode) &&
593
!namedTypes.ForInStatement.check(parentNode) &&
594
!(namedTypes.ForOfStatement &&
595
namedTypes.ForOfStatement.check(parentNode))) {
596
parts.push(";");
597
}
598
599
return concat(parts);
600
601
case "VariableDeclarator":
602
return n.init ? fromString(" = ").join([
603
path.call(print, "id"),
604
path.call(print, "init")
605
]) : path.call(print, "id");
606
607
case "WithStatement":
608
return concat([
609
"with (",
610
path.call(print, "object"),
611
") ",
612
path.call(print, "body")
613
]);
614
615
case "IfStatement":
616
var con = adjustClause(path.call(print, "consequent"), options),
617
parts = ["if (", path.call(print, "test"), ")", con];
618
619
if (n.alternate)
620
parts.push(
621
endsWithBrace(con) ? " else" : "\nelse",
622
adjustClause(path.call(print, "alternate"), options));
623
624
return concat(parts);
625
626
case "ForStatement":
627
// TODO Get the for (;;) case right.
628
var init = path.call(print, "init"),
629
sep = init.length > 1 ? ";\n" : "; ",
630
forParen = "for (",
631
indented = fromString(sep).join([
632
init,
633
path.call(print, "test"),
634
path.call(print, "update")
635
]).indentTail(forParen.length),
636
head = concat([forParen, indented, ")"]),
637
clause = adjustClause(path.call(print, "body"), options),
638
parts = [head];
639
640
if (head.length > 1) {
641
parts.push("\n");
642
clause = clause.trimLeft();
643
}
644
645
parts.push(clause);
646
647
return concat(parts);
648
649
case "WhileStatement":
650
return concat([
651
"while (",
652
path.call(print, "test"),
653
")",
654
adjustClause(path.call(print, "body"), options)
655
]);
656
657
case "ForInStatement":
658
// Note: esprima can't actually parse "for each (".
659
return concat([
660
n.each ? "for each (" : "for (",
661
path.call(print, "left"),
662
" in ",
663
path.call(print, "right"),
664
")",
665
adjustClause(path.call(print, "body"), options)
666
]);
667
668
case "ForOfStatement":
669
return concat([
670
"for (",
671
path.call(print, "left"),
672
" of ",
673
path.call(print, "right"),
674
")",
675
adjustClause(path.call(print, "body"), options)
676
]);
677
678
case "DoWhileStatement":
679
var doBody = concat([
680
"do",
681
adjustClause(path.call(print, "body"), options)
682
]), parts = [doBody];
683
684
if (endsWithBrace(doBody))
685
parts.push(" while");
686
else
687
parts.push("\nwhile");
688
689
parts.push(" (", path.call(print, "test"), ");");
690
691
return concat(parts);
692
693
case "BreakStatement":
694
var parts = ["break"];
695
if (n.label)
696
parts.push(" ", path.call(print, "label"));
697
parts.push(";");
698
return concat(parts);
699
700
case "ContinueStatement":
701
var parts = ["continue"];
702
if (n.label)
703
parts.push(" ", path.call(print, "label"));
704
parts.push(";");
705
return concat(parts);
706
707
case "LabeledStatement":
708
return concat([
709
path.call(print, "label"),
710
":\n",
711
path.call(print, "body")
712
]);
713
714
case "TryStatement":
715
var parts = [
716
"try ",
717
path.call(print, "block")
718
];
719
720
path.each(function(handlerPath) {
721
parts.push(" ", print(handlerPath));
722
}, "handlers");
723
724
if (n.finalizer)
725
parts.push(" finally ", path.call(print, "finalizer"));
726
727
return concat(parts);
728
729
case "CatchClause":
730
var parts = ["catch (", path.call(print, "param")];
731
732
if (n.guard)
733
// Note: esprima does not recognize conditional catch clauses.
734
parts.push(" if ", path.call(print, "guard"));
735
736
parts.push(") ", path.call(print, "body"));
737
738
return concat(parts);
739
740
case "ThrowStatement":
741
return concat(["throw ", path.call(print, "argument"), ";"]);
742
743
case "SwitchStatement":
744
return concat([
745
"switch (",
746
path.call(print, "discriminant"),
747
") {\n",
748
fromString("\n").join(path.map(print, "cases")),
749
"\n}"
750
]);
751
752
// Note: ignoring n.lexical because it has no printing consequences.
753
754
case "SwitchCase":
755
var parts = [];
756
757
if (n.test)
758
parts.push("case ", path.call(print, "test"), ":");
759
else
760
parts.push("default:");
761
762
if (n.consequent.length > 0) {
763
parts.push("\n", path.call(function(consequentPath) {
764
return printStatementSequence(consequentPath, options, print);
765
}, "consequent").indent(options.tabWidth));
766
}
767
768
return concat(parts);
769
770
case "DebuggerStatement":
771
return fromString("debugger;");
772
773
// XJS extensions below.
774
775
case "XJSAttribute":
776
var parts = [path.call(print, "name")];
777
if (n.value)
778
parts.push("=", path.call(print, "value"));
779
return concat(parts);
780
781
case "XJSIdentifier":
782
return fromString(n.name, options);
783
784
case "XJSNamespacedName":
785
return fromString(":").join([
786
path.call(print, "namespace"),
787
path.call(print, "name")
788
]);
789
790
case "XJSMemberExpression":
791
return fromString(".").join([
792
path.call(print, "object"),
793
path.call(print, "property")
794
]);
795
796
case "XJSSpreadAttribute":
797
return concat(["{...", path.call(print, "argument"), "}"]);
798
799
case "XJSExpressionContainer":
800
return concat(["{", path.call(print, "expression"), "}"]);
801
802
case "XJSElement":
803
var openingLines = path.call(print, "openingElement");
804
805
if (n.openingElement.selfClosing) {
806
assert.ok(!n.closingElement);
807
return openingLines;
808
}
809
810
var childLines = concat(
811
path.map(function(childPath) {
812
var child = childPath.getValue();
813
814
if (namedTypes.Literal.check(child) &&
815
typeof child.value === "string") {
816
if (/\S/.test(child.value)) {
817
return child.value.replace(/^\s+|\s+$/g, "");
818
} else if (/\n/.test(child.value)) {
819
return "\n";
820
}
821
}
822
823
return print(childPath);
824
}, "children")
825
).indentTail(options.tabWidth);
826
827
var closingLines = path.call(print, "closingElement");
828
829
return concat([
830
openingLines,
831
childLines,
832
closingLines
833
]);
834
835
case "XJSOpeningElement":
836
var parts = ["<", path.call(print, "name")];
837
var attrParts = [];
838
839
path.each(function(attrPath) {
840
attrParts.push(" ", print(attrPath));
841
}, "attributes");
842
843
var attrLines = concat(attrParts);
844
845
var needLineWrap = (
846
attrLines.length > 1 ||
847
attrLines.getLineLength(1) > options.wrapColumn
848
);
849
850
if (needLineWrap) {
851
attrParts.forEach(function(part, i) {
852
if (part === " ") {
853
assert.strictEqual(i % 2, 0);
854
attrParts[i] = "\n";
855
}
856
});
857
858
attrLines = concat(attrParts).indentTail(options.tabWidth);
859
}
860
861
parts.push(attrLines, n.selfClosing ? " />" : ">");
862
863
return concat(parts);
864
865
case "XJSClosingElement":
866
return concat(["</", path.call(print, "name"), ">"]);
867
868
case "XJSText":
869
return fromString(n.value, options);
870
871
case "XJSEmptyExpression":
872
return fromString("");
873
874
case "TypeAnnotatedIdentifier":
875
return concat([
876
path.call(print, "annotation"),
877
" ",
878
path.call(print, "identifier")
879
]);
880
881
case "ClassBody":
882
if (n.body.length === 0) {
883
return fromString("{}");
884
}
885
886
return concat([
887
"{\n",
888
path.call(function(bodyPath) {
889
return printStatementSequence(bodyPath, options, print);
890
}, "body").indent(options.tabWidth),
891
"\n}"
892
]);
893
894
case "ClassPropertyDefinition":
895
var parts = ["static ", path.call(print, "definition")];
896
if (!namedTypes.MethodDefinition.check(n.definition))
897
parts.push(";");
898
return concat(parts);
899
900
case "ClassProperty":
901
return concat([path.call(print, "id"), ";"]);
902
903
case "ClassDeclaration":
904
case "ClassExpression":
905
var parts = ["class"];
906
907
if (n.id)
908
parts.push(" ", path.call(print, "id"));
909
910
if (n.superClass)
911
parts.push(" extends ", path.call(print, "superClass"));
912
913
parts.push(" ", path.call(print, "body"));
914
915
return concat(parts);
916
917
// These types are unprintable because they serve as abstract
918
// supertypes for other (printable) types.
919
case "Node":
920
case "Printable":
921
case "SourceLocation":
922
case "Position":
923
case "Statement":
924
case "Function":
925
case "Pattern":
926
case "Expression":
927
case "Declaration":
928
case "Specifier":
929
case "NamedSpecifier":
930
case "Block": // Block comment.
931
case "Line": // Line comment.
932
throw new Error("unprintable type: " + JSON.stringify(n.type));
933
934
// Unhandled types below. If encountered, nodes of these types should
935
// be either left alone or desugared into AST types that are fully
936
// supported by the pretty-printer.
937
938
case "ClassHeritage": // TODO
939
case "ComprehensionBlock": // TODO
940
case "ComprehensionExpression": // TODO
941
case "Glob": // TODO
942
case "TaggedTemplateExpression": // TODO
943
case "TemplateElement": // TODO
944
case "TemplateLiteral": // TODO
945
case "GeneratorExpression": // TODO
946
case "LetStatement": // TODO
947
case "LetExpression": // TODO
948
case "GraphExpression": // TODO
949
case "GraphIndexExpression": // TODO
950
951
// Type Annotations for Facebook Flow, typically stripped out or
952
// transformed away before printing.
953
case "AnyTypeAnnotation": // TODO
954
case "ArrayTypeAnnotation": // TODO
955
case "BooleanTypeAnnotation": // TODO
956
case "ClassImplements": // TODO
957
case "DeclareClass": // TODO
958
case "DeclareFunction": // TODO
959
case "DeclareModule": // TODO
960
case "DeclareVariable": // TODO
961
case "FunctionTypeAnnotation": // TODO
962
case "FunctionTypeParam": // TODO
963
case "GenericTypeAnnotation": // TODO
964
case "InterfaceDeclaration": // TODO
965
case "InterfaceExtends": // TODO
966
case "IntersectionTypeAnnotation": // TODO
967
case "MemberTypeAnnotation": // TODO
968
case "NullableTypeAnnotation": // TODO
969
case "NumberTypeAnnotation": // TODO
970
case "ObjectTypeAnnotation": // TODO
971
case "ObjectTypeCallProperty": // TODO
972
case "ObjectTypeIndexer": // TODO
973
case "ObjectTypeProperty": // TODO
974
case "QualifiedTypeIdentifier": // TODO
975
case "StringLiteralTypeAnnotation": // TODO
976
case "StringTypeAnnotation": // TODO
977
case "TupleTypeAnnotation": // TODO
978
case "Type": // TODO
979
case "TypeAlias": // TODO
980
case "TypeAnnotation": // TODO
981
case "TypeCastExpression": // TODO
982
case "TypeParameterDeclaration": // TODO
983
case "TypeParameterInstantiation": // TODO
984
case "TypeofTypeAnnotation": // TODO
985
case "UnionTypeAnnotation": // TODO
986
case "VoidTypeAnnotation": // TODO
987
988
// XML types that nobody cares about or needs to print.
989
case "XMLDefaultDeclaration":
990
case "XMLAnyName":
991
case "XMLQualifiedIdentifier":
992
case "XMLFunctionQualifiedIdentifier":
993
case "XMLAttributeSelector":
994
case "XMLFilterExpression":
995
case "XML":
996
case "XMLElement":
997
case "XMLList":
998
case "XMLEscape":
999
case "XMLText":
1000
case "XMLStartTag":
1001
case "XMLEndTag":
1002
case "XMLPointTag":
1003
case "XMLName":
1004
case "XMLAttribute":
1005
case "XMLCdata":
1006
case "XMLComment":
1007
case "XMLProcessingInstruction":
1008
default:
1009
debugger;
1010
throw new Error("unknown type: " + JSON.stringify(n.type));
1011
}
1012
1013
return p;
1014
}
1015
1016
function printStatementSequence(path, options, print) {
1017
var inClassBody =
1018
namedTypes.ClassBody &&
1019
namedTypes.ClassBody.check(path.getParentNode());
1020
1021
var filtered = [];
1022
path.each(function(stmtPath) {
1023
var i = stmtPath.getName();
1024
var stmt = stmtPath.getValue();
1025
1026
// Just in case the AST has been modified to contain falsy
1027
// "statements," it's safer simply to skip them.
1028
if (!stmt) {
1029
return;
1030
}
1031
1032
// Skip printing EmptyStatement nodes to avoid leaving stray
1033
// semicolons lying around.
1034
if (stmt.type === "EmptyStatement") {
1035
return;
1036
}
1037
1038
if (!inClassBody) {
1039
namedTypes.Statement.assert(stmt);
1040
}
1041
1042
// We can't hang onto stmtPath outside of this function, because
1043
// it's just a reference to a mutable FastPath object, so we have
1044
// to go ahead and print it here.
1045
filtered.push({
1046
node: stmt,
1047
printed: print(stmtPath)
1048
});
1049
});
1050
1051
var prevTrailingSpace = null;
1052
var len = filtered.length;
1053
var parts = [];
1054
1055
filtered.forEach(function(info, i) {
1056
var printed = info.printed;
1057
var stmt = info.node;
1058
var needSemicolon = true;
1059
var multiLine = printed.length > 1;
1060
var notFirst = i > 0;
1061
var notLast = i < len - 1;
1062
var leadingSpace;
1063
var trailingSpace;
1064
1065
if (inClassBody) {
1066
if (namedTypes.MethodDefinition.check(stmt) ||
1067
(namedTypes.ClassPropertyDefinition.check(stmt) &&
1068
namedTypes.MethodDefinition.check(stmt.definition))) {
1069
needSemicolon = false;
1070
}
1071
}
1072
1073
if (needSemicolon) {
1074
// Try to add a semicolon to anything that isn't a method in a
1075
// class body.
1076
printed = maybeAddSemicolon(printed);
1077
}
1078
1079
var trueLoc = options.reuseWhitespace && getTrueLoc(stmt);
1080
var lines = trueLoc && trueLoc.lines;
1081
1082
if (notFirst) {
1083
if (lines) {
1084
var beforeStart = lines.skipSpaces(trueLoc.start, true);
1085
var beforeStartLine = beforeStart ? beforeStart.line : 1;
1086
var leadingGap = trueLoc.start.line - beforeStartLine;
1087
leadingSpace = Array(leadingGap + 1).join("\n");
1088
} else {
1089
leadingSpace = multiLine ? "\n\n" : "\n";
1090
}
1091
} else {
1092
leadingSpace = "";
1093
}
1094
1095
if (notLast) {
1096
if (lines) {
1097
var afterEnd = lines.skipSpaces(trueLoc.end);
1098
var afterEndLine = afterEnd ? afterEnd.line : lines.length;
1099
var trailingGap = afterEndLine - trueLoc.end.line;
1100
trailingSpace = Array(trailingGap + 1).join("\n");
1101
} else {
1102
trailingSpace = multiLine ? "\n\n" : "\n";
1103
}
1104
} else {
1105
trailingSpace = "";
1106
}
1107
1108
parts.push(
1109
maxSpace(prevTrailingSpace, leadingSpace),
1110
printed
1111
);
1112
1113
if (notLast) {
1114
prevTrailingSpace = trailingSpace;
1115
} else if (trailingSpace) {
1116
parts.push(trailingSpace);
1117
}
1118
});
1119
1120
return concat(parts);
1121
}
1122
1123
function getTrueLoc(node) {
1124
// It's possible that node is newly-created (not parsed by Esprima),
1125
// in which case it probably won't have a .loc property (or an
1126
// .original property for that matter). That's fine; we'll just
1127
// pretty-print it as usual.
1128
if (!node.loc) {
1129
return null;
1130
}
1131
1132
if (!node.comments) {
1133
// If the node has no comments, regard node.loc as true.
1134
return node.loc;
1135
}
1136
1137
var start = node.loc.start;
1138
var end = node.loc.end;
1139
1140
// If the node has any comments, their locations might contribute to
1141
// the true start/end positions of the node.
1142
node.comments.forEach(function(comment) {
1143
if (comment.loc) {
1144
if (util.comparePos(comment.loc.start, start) < 0) {
1145
start = comment.loc.start;
1146
}
1147
1148
if (util.comparePos(end, comment.loc.end) < 0) {
1149
end = comment.loc.end;
1150
}
1151
}
1152
});
1153
1154
return {
1155
lines: node.loc.lines,
1156
start: start,
1157
end: end
1158
};
1159
}
1160
1161
function maxSpace(s1, s2) {
1162
if (!s1 && !s2) {
1163
return fromString("");
1164
}
1165
1166
if (!s1) {
1167
return fromString(s2);
1168
}
1169
1170
if (!s2) {
1171
return fromString(s1);
1172
}
1173
1174
var spaceLines1 = fromString(s1);
1175
var spaceLines2 = fromString(s2);
1176
1177
if (spaceLines2.length > spaceLines1.length) {
1178
return spaceLines2;
1179
}
1180
1181
return spaceLines1;
1182
}
1183
1184
function printMethod(path, options, print) {
1185
var node = path.getNode();
1186
var kind = node.kind;
1187
var parts = [];
1188
1189
namedTypes.FunctionExpression.assert(node.value);
1190
1191
if (node.value.async) {
1192
parts.push("async ");
1193
}
1194
1195
if (!kind || kind === "init") {
1196
if (node.value.generator) {
1197
parts.push("*");
1198
}
1199
} else {
1200
assert.ok(kind === "get" || kind === "set");
1201
parts.push(kind, " ");
1202
}
1203
1204
parts.push(
1205
path.call(print, "key"),
1206
"(",
1207
path.call(function(valuePath) {
1208
return printFunctionParams(valuePath, options, print);
1209
}, "value"),
1210
") ",
1211
path.call(print, "value", "body")
1212
);
1213
1214
return concat(parts);
1215
}
1216
1217
function printArgumentsList(path, options, print) {
1218
var printed = path.map(print, "arguments");
1219
1220
var joined = fromString(", ").join(printed);
1221
if (joined.getLineLength(1) > options.wrapColumn) {
1222
joined = fromString(",\n").join(printed);
1223
return concat(["(\n", joined.indent(options.tabWidth), "\n)"]);
1224
}
1225
1226
return concat(["(", joined, ")"]);
1227
}
1228
1229
function printFunctionParams(path, options, print) {
1230
var fun = path.getValue();
1231
namedTypes.Function.assert(fun);
1232
1233
var printed = path.map(print, "params");
1234
1235
if (fun.defaults) {
1236
path.each(function(defExprPath) {
1237
var i = defExprPath.getName();
1238
var p = printed[i];
1239
if (p && defExprPath.getValue()) {
1240
printed[i] = concat([p, "=", print(defExprPath)]);
1241
}
1242
}, "defaults");
1243
}
1244
1245
if (fun.rest) {
1246
printed.push(concat(["...", path.call(print, "rest")]));
1247
}
1248
1249
var joined = fromString(", ").join(printed);
1250
if (joined.length > 1 ||
1251
joined.getLineLength(1) > options.wrapColumn) {
1252
joined = fromString(",\n").join(printed);
1253
return concat(["\n", joined.indent(options.tabWidth)]);
1254
}
1255
1256
return joined;
1257
}
1258
1259
function adjustClause(clause, options) {
1260
if (clause.length > 1)
1261
return concat([" ", clause]);
1262
1263
return concat([
1264
"\n",
1265
maybeAddSemicolon(clause).indent(options.tabWidth)
1266
]);
1267
}
1268
1269
function lastNonSpaceCharacter(lines) {
1270
var pos = lines.lastPos();
1271
do {
1272
var ch = lines.charAt(pos);
1273
if (/\S/.test(ch))
1274
return ch;
1275
} while (lines.prevPos(pos));
1276
}
1277
1278
function endsWithBrace(lines) {
1279
return lastNonSpaceCharacter(lines) === "}";
1280
}
1281
1282
function swapQuotes(str) {
1283
return str.replace(/['"]/g, function(m) {
1284
return m === '"' ? '\'' : '"';
1285
});
1286
}
1287
1288
function nodeStr(n, options) {
1289
namedTypes.Literal.assert(n);
1290
isString.assert(n.value);
1291
switch (options.quote) {
1292
case "auto":
1293
var double = JSON.stringify(n.value);
1294
var single = swapQuotes(JSON.stringify(swapQuotes(n.value)));
1295
return double.length > single.length ? single : double;
1296
case "single":
1297
return swapQuotes(JSON.stringify(swapQuotes(n.value)));
1298
case "double":
1299
default:
1300
return JSON.stringify(n.value);
1301
}
1302
}
1303
1304
function maybeAddSemicolon(lines) {
1305
var eoc = lastNonSpaceCharacter(lines);
1306
if (!eoc || "\n};".indexOf(eoc) < 0)
1307
return concat([lines, ";"]);
1308
return lines;
1309
}
1310
1311