Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
80556 views
1
/*
2
Copyright (C) 2012-2014 Yusuke Suzuki <[email protected]>
3
Copyright (C) 2015 Ingvar Stepanyan <[email protected]>
4
Copyright (C) 2014 Ivan Nikulin <[email protected]>
5
Copyright (C) 2012-2013 Michael Ficarra <[email protected]>
6
Copyright (C) 2012-2013 Mathias Bynens <[email protected]>
7
Copyright (C) 2013 Irakli Gozalishvili <[email protected]>
8
Copyright (C) 2012 Robert Gust-Bardon <[email protected]>
9
Copyright (C) 2012 John Freeman <[email protected]>
10
Copyright (C) 2011-2012 Ariya Hidayat <[email protected]>
11
Copyright (C) 2012 Joost-Wim Boekesteijn <[email protected]>
12
Copyright (C) 2012 Kris Kowal <[email protected]>
13
Copyright (C) 2012 Arpad Borsos <[email protected]>
14
15
Redistribution and use in source and binary forms, with or without
16
modification, are permitted provided that the following conditions are met:
17
18
* Redistributions of source code must retain the above copyright
19
notice, this list of conditions and the following disclaimer.
20
* Redistributions in binary form must reproduce the above copyright
21
notice, this list of conditions and the following disclaimer in the
22
documentation and/or other materials provided with the distribution.
23
24
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
25
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27
ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
28
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
29
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
30
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
31
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
32
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
33
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34
*/
35
36
/*global exports:true, require:true, global:true*/
37
(function () {
38
'use strict';
39
40
var Syntax,
41
Precedence,
42
BinaryPrecedence,
43
SourceNode,
44
estraverse,
45
esutils,
46
isArray,
47
base,
48
indent,
49
json,
50
renumber,
51
hexadecimal,
52
quotes,
53
escapeless,
54
newline,
55
space,
56
parentheses,
57
semicolons,
58
safeConcatenation,
59
directive,
60
extra,
61
parse,
62
sourceMap,
63
sourceCode,
64
preserveBlankLines,
65
FORMAT_MINIFY,
66
FORMAT_DEFAULTS;
67
68
estraverse = require('estraverse');
69
esutils = require('esutils');
70
71
Syntax = estraverse.Syntax;
72
73
// Generation is done by generateExpression.
74
function isExpression(node) {
75
return CodeGenerator.Expression.hasOwnProperty(node.type);
76
}
77
78
// Generation is done by generateStatement.
79
function isStatement(node) {
80
return CodeGenerator.Statement.hasOwnProperty(node.type);
81
}
82
83
Precedence = {
84
Sequence: 0,
85
Yield: 1,
86
Await: 1,
87
Assignment: 1,
88
Conditional: 2,
89
ArrowFunction: 2,
90
LogicalOR: 3,
91
LogicalAND: 4,
92
BitwiseOR: 5,
93
BitwiseXOR: 6,
94
BitwiseAND: 7,
95
Equality: 8,
96
Relational: 9,
97
BitwiseSHIFT: 10,
98
Additive: 11,
99
Multiplicative: 12,
100
Unary: 13,
101
Postfix: 14,
102
Call: 15,
103
New: 16,
104
TaggedTemplate: 17,
105
Member: 18,
106
Primary: 19
107
};
108
109
BinaryPrecedence = {
110
'||': Precedence.LogicalOR,
111
'&&': Precedence.LogicalAND,
112
'|': Precedence.BitwiseOR,
113
'^': Precedence.BitwiseXOR,
114
'&': Precedence.BitwiseAND,
115
'==': Precedence.Equality,
116
'!=': Precedence.Equality,
117
'===': Precedence.Equality,
118
'!==': Precedence.Equality,
119
'is': Precedence.Equality,
120
'isnt': Precedence.Equality,
121
'<': Precedence.Relational,
122
'>': Precedence.Relational,
123
'<=': Precedence.Relational,
124
'>=': Precedence.Relational,
125
'in': Precedence.Relational,
126
'instanceof': Precedence.Relational,
127
'<<': Precedence.BitwiseSHIFT,
128
'>>': Precedence.BitwiseSHIFT,
129
'>>>': Precedence.BitwiseSHIFT,
130
'+': Precedence.Additive,
131
'-': Precedence.Additive,
132
'*': Precedence.Multiplicative,
133
'%': Precedence.Multiplicative,
134
'/': Precedence.Multiplicative
135
};
136
137
//Flags
138
var F_ALLOW_IN = 1,
139
F_ALLOW_CALL = 1 << 1,
140
F_ALLOW_UNPARATH_NEW = 1 << 2,
141
F_FUNC_BODY = 1 << 3,
142
F_DIRECTIVE_CTX = 1 << 4,
143
F_SEMICOLON_OPT = 1 << 5;
144
145
//Expression flag sets
146
//NOTE: Flag order:
147
// F_ALLOW_IN
148
// F_ALLOW_CALL
149
// F_ALLOW_UNPARATH_NEW
150
var E_FTT = F_ALLOW_CALL | F_ALLOW_UNPARATH_NEW,
151
E_TTF = F_ALLOW_IN | F_ALLOW_CALL,
152
E_TTT = F_ALLOW_IN | F_ALLOW_CALL | F_ALLOW_UNPARATH_NEW,
153
E_TFF = F_ALLOW_IN,
154
E_FFT = F_ALLOW_UNPARATH_NEW,
155
E_TFT = F_ALLOW_IN | F_ALLOW_UNPARATH_NEW;
156
157
//Statement flag sets
158
//NOTE: Flag order:
159
// F_ALLOW_IN
160
// F_FUNC_BODY
161
// F_DIRECTIVE_CTX
162
// F_SEMICOLON_OPT
163
var S_TFFF = F_ALLOW_IN,
164
S_TFFT = F_ALLOW_IN | F_SEMICOLON_OPT,
165
S_FFFF = 0x00,
166
S_TFTF = F_ALLOW_IN | F_DIRECTIVE_CTX,
167
S_TTFF = F_ALLOW_IN | F_FUNC_BODY;
168
169
function getDefaultOptions() {
170
// default options
171
return {
172
indent: null,
173
base: null,
174
parse: null,
175
comment: false,
176
format: {
177
indent: {
178
style: ' ',
179
base: 0,
180
adjustMultilineComment: false
181
},
182
newline: '\n',
183
space: ' ',
184
json: false,
185
renumber: false,
186
hexadecimal: false,
187
quotes: 'single',
188
escapeless: false,
189
compact: false,
190
parentheses: true,
191
semicolons: true,
192
safeConcatenation: false,
193
preserveBlankLines: false
194
},
195
moz: {
196
comprehensionExpressionStartsWithAssignment: false,
197
starlessGenerator: false
198
},
199
sourceMap: null,
200
sourceMapRoot: null,
201
sourceMapWithCode: false,
202
directive: false,
203
raw: true,
204
verbatim: null,
205
sourceCode: null
206
};
207
}
208
209
function stringRepeat(str, num) {
210
var result = '';
211
212
for (num |= 0; num > 0; num >>>= 1, str += str) {
213
if (num & 1) {
214
result += str;
215
}
216
}
217
218
return result;
219
}
220
221
isArray = Array.isArray;
222
if (!isArray) {
223
isArray = function isArray(array) {
224
return Object.prototype.toString.call(array) === '[object Array]';
225
};
226
}
227
228
function hasLineTerminator(str) {
229
return (/[\r\n]/g).test(str);
230
}
231
232
function endsWithLineTerminator(str) {
233
var len = str.length;
234
return len && esutils.code.isLineTerminator(str.charCodeAt(len - 1));
235
}
236
237
function merge(target, override) {
238
var key;
239
for (key in override) {
240
if (override.hasOwnProperty(key)) {
241
target[key] = override[key];
242
}
243
}
244
return target;
245
}
246
247
function updateDeeply(target, override) {
248
var key, val;
249
250
function isHashObject(target) {
251
return typeof target === 'object' && target instanceof Object && !(target instanceof RegExp);
252
}
253
254
for (key in override) {
255
if (override.hasOwnProperty(key)) {
256
val = override[key];
257
if (isHashObject(val)) {
258
if (isHashObject(target[key])) {
259
updateDeeply(target[key], val);
260
} else {
261
target[key] = updateDeeply({}, val);
262
}
263
} else {
264
target[key] = val;
265
}
266
}
267
}
268
return target;
269
}
270
271
function generateNumber(value) {
272
var result, point, temp, exponent, pos;
273
274
if (value !== value) {
275
throw new Error('Numeric literal whose value is NaN');
276
}
277
if (value < 0 || (value === 0 && 1 / value < 0)) {
278
throw new Error('Numeric literal whose value is negative');
279
}
280
281
if (value === 1 / 0) {
282
return json ? 'null' : renumber ? '1e400' : '1e+400';
283
}
284
285
result = '' + value;
286
if (!renumber || result.length < 3) {
287
return result;
288
}
289
290
point = result.indexOf('.');
291
if (!json && result.charCodeAt(0) === 0x30 /* 0 */ && point === 1) {
292
point = 0;
293
result = result.slice(1);
294
}
295
temp = result;
296
result = result.replace('e+', 'e');
297
exponent = 0;
298
if ((pos = temp.indexOf('e')) > 0) {
299
exponent = +temp.slice(pos + 1);
300
temp = temp.slice(0, pos);
301
}
302
if (point >= 0) {
303
exponent -= temp.length - point - 1;
304
temp = +(temp.slice(0, point) + temp.slice(point + 1)) + '';
305
}
306
pos = 0;
307
while (temp.charCodeAt(temp.length + pos - 1) === 0x30 /* 0 */) {
308
--pos;
309
}
310
if (pos !== 0) {
311
exponent -= pos;
312
temp = temp.slice(0, pos);
313
}
314
if (exponent !== 0) {
315
temp += 'e' + exponent;
316
}
317
if ((temp.length < result.length ||
318
(hexadecimal && value > 1e12 && Math.floor(value) === value && (temp = '0x' + value.toString(16)).length < result.length)) &&
319
+temp === value) {
320
result = temp;
321
}
322
323
return result;
324
}
325
326
// Generate valid RegExp expression.
327
// This function is based on https://github.com/Constellation/iv Engine
328
329
function escapeRegExpCharacter(ch, previousIsBackslash) {
330
// not handling '\' and handling \u2028 or \u2029 to unicode escape sequence
331
if ((ch & ~1) === 0x2028) {
332
return (previousIsBackslash ? 'u' : '\\u') + ((ch === 0x2028) ? '2028' : '2029');
333
} else if (ch === 10 || ch === 13) { // \n, \r
334
return (previousIsBackslash ? '' : '\\') + ((ch === 10) ? 'n' : 'r');
335
}
336
return String.fromCharCode(ch);
337
}
338
339
function generateRegExp(reg) {
340
var match, result, flags, i, iz, ch, characterInBrack, previousIsBackslash;
341
342
result = reg.toString();
343
344
if (reg.source) {
345
// extract flag from toString result
346
match = result.match(/\/([^/]*)$/);
347
if (!match) {
348
return result;
349
}
350
351
flags = match[1];
352
result = '';
353
354
characterInBrack = false;
355
previousIsBackslash = false;
356
for (i = 0, iz = reg.source.length; i < iz; ++i) {
357
ch = reg.source.charCodeAt(i);
358
359
if (!previousIsBackslash) {
360
if (characterInBrack) {
361
if (ch === 93) { // ]
362
characterInBrack = false;
363
}
364
} else {
365
if (ch === 47) { // /
366
result += '\\';
367
} else if (ch === 91) { // [
368
characterInBrack = true;
369
}
370
}
371
result += escapeRegExpCharacter(ch, previousIsBackslash);
372
previousIsBackslash = ch === 92; // \
373
} else {
374
// if new RegExp("\\\n') is provided, create /\n/
375
result += escapeRegExpCharacter(ch, previousIsBackslash);
376
// prevent like /\\[/]/
377
previousIsBackslash = false;
378
}
379
}
380
381
return '/' + result + '/' + flags;
382
}
383
384
return result;
385
}
386
387
function escapeAllowedCharacter(code, next) {
388
var hex;
389
390
if (code === 0x08 /* \b */) {
391
return '\\b';
392
}
393
394
if (code === 0x0C /* \f */) {
395
return '\\f';
396
}
397
398
if (code === 0x09 /* \t */) {
399
return '\\t';
400
}
401
402
hex = code.toString(16).toUpperCase();
403
if (json || code > 0xFF) {
404
return '\\u' + '0000'.slice(hex.length) + hex;
405
} else if (code === 0x0000 && !esutils.code.isDecimalDigit(next)) {
406
return '\\0';
407
} else if (code === 0x000B /* \v */) { // '\v'
408
return '\\x0B';
409
} else {
410
return '\\x' + '00'.slice(hex.length) + hex;
411
}
412
}
413
414
function escapeDisallowedCharacter(code) {
415
if (code === 0x5C /* \ */) {
416
return '\\\\';
417
}
418
419
if (code === 0x0A /* \n */) {
420
return '\\n';
421
}
422
423
if (code === 0x0D /* \r */) {
424
return '\\r';
425
}
426
427
if (code === 0x2028) {
428
return '\\u2028';
429
}
430
431
if (code === 0x2029) {
432
return '\\u2029';
433
}
434
435
throw new Error('Incorrectly classified character');
436
}
437
438
function escapeDirective(str) {
439
var i, iz, code, quote;
440
441
quote = quotes === 'double' ? '"' : '\'';
442
for (i = 0, iz = str.length; i < iz; ++i) {
443
code = str.charCodeAt(i);
444
if (code === 0x27 /* ' */) {
445
quote = '"';
446
break;
447
} else if (code === 0x22 /* " */) {
448
quote = '\'';
449
break;
450
} else if (code === 0x5C /* \ */) {
451
++i;
452
}
453
}
454
455
return quote + str + quote;
456
}
457
458
function escapeString(str) {
459
var result = '', i, len, code, singleQuotes = 0, doubleQuotes = 0, single, quote;
460
461
for (i = 0, len = str.length; i < len; ++i) {
462
code = str.charCodeAt(i);
463
if (code === 0x27 /* ' */) {
464
++singleQuotes;
465
} else if (code === 0x22 /* " */) {
466
++doubleQuotes;
467
} else if (code === 0x2F /* / */ && json) {
468
result += '\\';
469
} else if (esutils.code.isLineTerminator(code) || code === 0x5C /* \ */) {
470
result += escapeDisallowedCharacter(code);
471
continue;
472
} else if ((json && code < 0x20 /* SP */) || !(json || escapeless || (code >= 0x20 /* SP */ && code <= 0x7E /* ~ */))) {
473
result += escapeAllowedCharacter(code, str.charCodeAt(i + 1));
474
continue;
475
}
476
result += String.fromCharCode(code);
477
}
478
479
single = !(quotes === 'double' || (quotes === 'auto' && doubleQuotes < singleQuotes));
480
quote = single ? '\'' : '"';
481
482
if (!(single ? singleQuotes : doubleQuotes)) {
483
return quote + result + quote;
484
}
485
486
str = result;
487
result = quote;
488
489
for (i = 0, len = str.length; i < len; ++i) {
490
code = str.charCodeAt(i);
491
if ((code === 0x27 /* ' */ && single) || (code === 0x22 /* " */ && !single)) {
492
result += '\\';
493
}
494
result += String.fromCharCode(code);
495
}
496
497
return result + quote;
498
}
499
500
/**
501
* flatten an array to a string, where the array can contain
502
* either strings or nested arrays
503
*/
504
function flattenToString(arr) {
505
var i, iz, elem, result = '';
506
for (i = 0, iz = arr.length; i < iz; ++i) {
507
elem = arr[i];
508
result += isArray(elem) ? flattenToString(elem) : elem;
509
}
510
return result;
511
}
512
513
/**
514
* convert generated to a SourceNode when source maps are enabled.
515
*/
516
function toSourceNodeWhenNeeded(generated, node) {
517
if (!sourceMap) {
518
// with no source maps, generated is either an
519
// array or a string. if an array, flatten it.
520
// if a string, just return it
521
if (isArray(generated)) {
522
return flattenToString(generated);
523
} else {
524
return generated;
525
}
526
}
527
if (node == null) {
528
if (generated instanceof SourceNode) {
529
return generated;
530
} else {
531
node = {};
532
}
533
}
534
if (node.loc == null) {
535
return new SourceNode(null, null, sourceMap, generated, node.name || null);
536
}
537
return new SourceNode(node.loc.start.line, node.loc.start.column, (sourceMap === true ? node.loc.source || null : sourceMap), generated, node.name || null);
538
}
539
540
function noEmptySpace() {
541
return (space) ? space : ' ';
542
}
543
544
function join(left, right) {
545
var leftSource,
546
rightSource,
547
leftCharCode,
548
rightCharCode;
549
550
leftSource = toSourceNodeWhenNeeded(left).toString();
551
if (leftSource.length === 0) {
552
return [right];
553
}
554
555
rightSource = toSourceNodeWhenNeeded(right).toString();
556
if (rightSource.length === 0) {
557
return [left];
558
}
559
560
leftCharCode = leftSource.charCodeAt(leftSource.length - 1);
561
rightCharCode = rightSource.charCodeAt(0);
562
563
if ((leftCharCode === 0x2B /* + */ || leftCharCode === 0x2D /* - */) && leftCharCode === rightCharCode ||
564
esutils.code.isIdentifierPart(leftCharCode) && esutils.code.isIdentifierPart(rightCharCode) ||
565
leftCharCode === 0x2F /* / */ && rightCharCode === 0x69 /* i */) { // infix word operators all start with `i`
566
return [left, noEmptySpace(), right];
567
} else if (esutils.code.isWhiteSpace(leftCharCode) || esutils.code.isLineTerminator(leftCharCode) ||
568
esutils.code.isWhiteSpace(rightCharCode) || esutils.code.isLineTerminator(rightCharCode)) {
569
return [left, right];
570
}
571
return [left, space, right];
572
}
573
574
function addIndent(stmt) {
575
return [base, stmt];
576
}
577
578
function withIndent(fn) {
579
var previousBase;
580
previousBase = base;
581
base += indent;
582
fn(base);
583
base = previousBase;
584
}
585
586
function calculateSpaces(str) {
587
var i;
588
for (i = str.length - 1; i >= 0; --i) {
589
if (esutils.code.isLineTerminator(str.charCodeAt(i))) {
590
break;
591
}
592
}
593
return (str.length - 1) - i;
594
}
595
596
function adjustMultilineComment(value, specialBase) {
597
var array, i, len, line, j, spaces, previousBase, sn;
598
599
array = value.split(/\r\n|[\r\n]/);
600
spaces = Number.MAX_VALUE;
601
602
// first line doesn't have indentation
603
for (i = 1, len = array.length; i < len; ++i) {
604
line = array[i];
605
j = 0;
606
while (j < line.length && esutils.code.isWhiteSpace(line.charCodeAt(j))) {
607
++j;
608
}
609
if (spaces > j) {
610
spaces = j;
611
}
612
}
613
614
if (typeof specialBase !== 'undefined') {
615
// pattern like
616
// {
617
// var t = 20; /*
618
// * this is comment
619
// */
620
// }
621
previousBase = base;
622
if (array[1][spaces] === '*') {
623
specialBase += ' ';
624
}
625
base = specialBase;
626
} else {
627
if (spaces & 1) {
628
// /*
629
// *
630
// */
631
// If spaces are odd number, above pattern is considered.
632
// We waste 1 space.
633
--spaces;
634
}
635
previousBase = base;
636
}
637
638
for (i = 1, len = array.length; i < len; ++i) {
639
sn = toSourceNodeWhenNeeded(addIndent(array[i].slice(spaces)));
640
array[i] = sourceMap ? sn.join('') : sn;
641
}
642
643
base = previousBase;
644
645
return array.join('\n');
646
}
647
648
function generateComment(comment, specialBase) {
649
if (comment.type === 'Line') {
650
if (endsWithLineTerminator(comment.value)) {
651
return '//' + comment.value;
652
} else {
653
// Always use LineTerminator
654
var result = '//' + comment.value;
655
if (!preserveBlankLines) {
656
result += '\n';
657
}
658
return result;
659
}
660
}
661
if (extra.format.indent.adjustMultilineComment && /[\n\r]/.test(comment.value)) {
662
return adjustMultilineComment('/*' + comment.value + '*/', specialBase);
663
}
664
return '/*' + comment.value + '*/';
665
}
666
667
function addComments(stmt, result) {
668
var i, len, comment, save, tailingToStatement, specialBase, fragment,
669
extRange, range, prevRange, prefix, infix, suffix, count;
670
671
if (stmt.leadingComments && stmt.leadingComments.length > 0) {
672
save = result;
673
674
if (preserveBlankLines) {
675
comment = stmt.leadingComments[0];
676
result = [];
677
678
extRange = comment.extendedRange;
679
range = comment.range;
680
681
prefix = sourceCode.substring(extRange[0], range[0]);
682
count = (prefix.match(/\n/g) || []).length;
683
if (count > 0) {
684
result.push(stringRepeat('\n', count));
685
result.push(addIndent(generateComment(comment)));
686
} else {
687
result.push(prefix);
688
result.push(generateComment(comment));
689
}
690
691
prevRange = range;
692
693
for (i = 1, len = stmt.leadingComments.length; i < len; i++) {
694
comment = stmt.leadingComments[i];
695
range = comment.range;
696
697
infix = sourceCode.substring(prevRange[1], range[0]);
698
count = (infix.match(/\n/g) || []).length;
699
result.push(stringRepeat('\n', count));
700
result.push(addIndent(generateComment(comment)));
701
702
prevRange = range;
703
}
704
705
suffix = sourceCode.substring(range[1], extRange[1]);
706
count = (suffix.match(/\n/g) || []).length;
707
result.push(stringRepeat('\n', count));
708
} else {
709
comment = stmt.leadingComments[0];
710
result = [];
711
if (safeConcatenation && stmt.type === Syntax.Program && stmt.body.length === 0) {
712
result.push('\n');
713
}
714
result.push(generateComment(comment));
715
if (!endsWithLineTerminator(toSourceNodeWhenNeeded(result).toString())) {
716
result.push('\n');
717
}
718
719
for (i = 1, len = stmt.leadingComments.length; i < len; ++i) {
720
comment = stmt.leadingComments[i];
721
fragment = [generateComment(comment)];
722
if (!endsWithLineTerminator(toSourceNodeWhenNeeded(fragment).toString())) {
723
fragment.push('\n');
724
}
725
result.push(addIndent(fragment));
726
}
727
}
728
729
result.push(addIndent(save));
730
}
731
732
if (stmt.trailingComments) {
733
734
if (preserveBlankLines) {
735
comment = stmt.trailingComments[0];
736
extRange = comment.extendedRange;
737
range = comment.range;
738
739
prefix = sourceCode.substring(extRange[0], range[0]);
740
count = (prefix.match(/\n/g) || []).length;
741
742
if (count > 0) {
743
result.push(stringRepeat('\n', count));
744
result.push(addIndent(generateComment(comment)));
745
} else {
746
result.push(prefix);
747
result.push(generateComment(comment));
748
}
749
} else {
750
tailingToStatement = !endsWithLineTerminator(toSourceNodeWhenNeeded(result).toString());
751
specialBase = stringRepeat(' ', calculateSpaces(toSourceNodeWhenNeeded([base, result, indent]).toString()));
752
for (i = 0, len = stmt.trailingComments.length; i < len; ++i) {
753
comment = stmt.trailingComments[i];
754
if (tailingToStatement) {
755
// We assume target like following script
756
//
757
// var t = 20; /**
758
// * This is comment of t
759
// */
760
if (i === 0) {
761
// first case
762
result = [result, indent];
763
} else {
764
result = [result, specialBase];
765
}
766
result.push(generateComment(comment, specialBase));
767
} else {
768
result = [result, addIndent(generateComment(comment))];
769
}
770
if (i !== len - 1 && !endsWithLineTerminator(toSourceNodeWhenNeeded(result).toString())) {
771
result = [result, '\n'];
772
}
773
}
774
}
775
}
776
777
return result;
778
}
779
780
function generateBlankLines(start, end, result) {
781
var j, newlineCount = 0;
782
783
for (j = start; j < end; j++) {
784
if (sourceCode[j] === '\n') {
785
newlineCount++;
786
}
787
}
788
789
for (j = 1; j < newlineCount; j++) {
790
result.push(newline);
791
}
792
}
793
794
function parenthesize(text, current, should) {
795
if (current < should) {
796
return ['(', text, ')'];
797
}
798
return text;
799
}
800
801
function generateVerbatimString(string) {
802
var i, iz, result;
803
result = string.split(/\r\n|\n/);
804
for (i = 1, iz = result.length; i < iz; i++) {
805
result[i] = newline + base + result[i];
806
}
807
return result;
808
}
809
810
function generateVerbatim(expr, precedence) {
811
var verbatim, result, prec;
812
verbatim = expr[extra.verbatim];
813
814
if (typeof verbatim === 'string') {
815
result = parenthesize(generateVerbatimString(verbatim), Precedence.Sequence, precedence);
816
} else {
817
// verbatim is object
818
result = generateVerbatimString(verbatim.content);
819
prec = (verbatim.precedence != null) ? verbatim.precedence : Precedence.Sequence;
820
result = parenthesize(result, prec, precedence);
821
}
822
823
return toSourceNodeWhenNeeded(result, expr);
824
}
825
826
function CodeGenerator() {
827
}
828
829
// Helpers.
830
831
CodeGenerator.prototype.maybeBlock = function(stmt, flags) {
832
var result, noLeadingComment, that = this;
833
834
noLeadingComment = !extra.comment || !stmt.leadingComments;
835
836
if (stmt.type === Syntax.BlockStatement && noLeadingComment) {
837
return [space, this.generateStatement(stmt, flags)];
838
}
839
840
if (stmt.type === Syntax.EmptyStatement && noLeadingComment) {
841
return ';';
842
}
843
844
withIndent(function () {
845
result = [
846
newline,
847
addIndent(that.generateStatement(stmt, flags))
848
];
849
});
850
851
return result;
852
};
853
854
CodeGenerator.prototype.maybeBlockSuffix = function (stmt, result) {
855
var ends = endsWithLineTerminator(toSourceNodeWhenNeeded(result).toString());
856
if (stmt.type === Syntax.BlockStatement && (!extra.comment || !stmt.leadingComments) && !ends) {
857
return [result, space];
858
}
859
if (ends) {
860
return [result, base];
861
}
862
return [result, newline, base];
863
};
864
865
function generateIdentifier(node) {
866
return toSourceNodeWhenNeeded(node.name, node);
867
}
868
869
function generateAsyncPrefix(node, spaceRequired) {
870
return node.async ? 'async' + (spaceRequired ? noEmptySpace() : space) : '';
871
}
872
873
function generateStarSuffix(node) {
874
var isGenerator = node.generator && !extra.moz.starlessGenerator;
875
return isGenerator ? '*' + space : '';
876
}
877
878
function generateMethodPrefix(prop) {
879
var func = prop.value;
880
if (func.async) {
881
return generateAsyncPrefix(func, !prop.computed);
882
} else {
883
// avoid space before method name
884
return generateStarSuffix(func) ? '*' : '';
885
}
886
}
887
888
CodeGenerator.prototype.generatePattern = function (node, precedence, flags) {
889
if (node.type === Syntax.Identifier) {
890
return generateIdentifier(node);
891
}
892
return this.generateExpression(node, precedence, flags);
893
};
894
895
CodeGenerator.prototype.generateFunctionParams = function (node) {
896
var i, iz, result, hasDefault;
897
898
hasDefault = false;
899
900
if (node.type === Syntax.ArrowFunctionExpression &&
901
!node.rest && (!node.defaults || node.defaults.length === 0) &&
902
node.params.length === 1 && node.params[0].type === Syntax.Identifier) {
903
// arg => { } case
904
result = [generateAsyncPrefix(node, true), generateIdentifier(node.params[0])];
905
} else {
906
result = node.type === Syntax.ArrowFunctionExpression ? [generateAsyncPrefix(node, false)] : [];
907
result.push('(');
908
if (node.defaults) {
909
hasDefault = true;
910
}
911
for (i = 0, iz = node.params.length; i < iz; ++i) {
912
if (hasDefault && node.defaults[i]) {
913
// Handle default values.
914
result.push(this.generateAssignment(node.params[i], node.defaults[i], '=', Precedence.Assignment, E_TTT));
915
} else {
916
result.push(this.generatePattern(node.params[i], Precedence.Assignment, E_TTT));
917
}
918
if (i + 1 < iz) {
919
result.push(',' + space);
920
}
921
}
922
923
if (node.rest) {
924
if (node.params.length) {
925
result.push(',' + space);
926
}
927
result.push('...');
928
result.push(generateIdentifier(node.rest));
929
}
930
931
result.push(')');
932
}
933
934
return result;
935
};
936
937
CodeGenerator.prototype.generateFunctionBody = function (node) {
938
var result, expr;
939
940
result = this.generateFunctionParams(node);
941
942
if (node.type === Syntax.ArrowFunctionExpression) {
943
result.push(space);
944
result.push('=>');
945
}
946
947
if (node.expression) {
948
result.push(space);
949
expr = this.generateExpression(node.body, Precedence.Assignment, E_TTT);
950
if (expr.toString().charAt(0) === '{') {
951
expr = ['(', expr, ')'];
952
}
953
result.push(expr);
954
} else {
955
result.push(this.maybeBlock(node.body, S_TTFF));
956
}
957
958
return result;
959
};
960
961
CodeGenerator.prototype.generateIterationForStatement = function (operator, stmt, flags) {
962
var result = ['for' + space + '('], that = this;
963
withIndent(function () {
964
if (stmt.left.type === Syntax.VariableDeclaration) {
965
withIndent(function () {
966
result.push(stmt.left.kind + noEmptySpace());
967
result.push(that.generateStatement(stmt.left.declarations[0], S_FFFF));
968
});
969
} else {
970
result.push(that.generateExpression(stmt.left, Precedence.Call, E_TTT));
971
}
972
973
result = join(result, operator);
974
result = [join(
975
result,
976
that.generateExpression(stmt.right, Precedence.Sequence, E_TTT)
977
), ')'];
978
});
979
result.push(this.maybeBlock(stmt.body, flags));
980
return result;
981
};
982
983
CodeGenerator.prototype.generatePropertyKey = function (expr, computed) {
984
var result = [];
985
986
if (computed) {
987
result.push('[');
988
}
989
990
result.push(this.generateExpression(expr, Precedence.Sequence, E_TTT));
991
if (computed) {
992
result.push(']');
993
}
994
995
return result;
996
};
997
998
CodeGenerator.prototype.generateAssignment = function (left, right, operator, precedence, flags) {
999
if (Precedence.Assignment < precedence) {
1000
flags |= F_ALLOW_IN;
1001
}
1002
1003
return parenthesize(
1004
[
1005
this.generateExpression(left, Precedence.Call, flags),
1006
space + operator + space,
1007
this.generateExpression(right, Precedence.Assignment, flags)
1008
],
1009
Precedence.Assignment,
1010
precedence
1011
);
1012
};
1013
1014
CodeGenerator.prototype.semicolon = function (flags) {
1015
if (!semicolons && flags & F_SEMICOLON_OPT) {
1016
return '';
1017
}
1018
return ';';
1019
};
1020
1021
// Statements.
1022
1023
CodeGenerator.Statement = {
1024
1025
BlockStatement: function (stmt, flags) {
1026
var range, content, result = ['{', newline], that = this;
1027
1028
withIndent(function () {
1029
// handle functions without any code
1030
if (stmt.body.length === 0 && preserveBlankLines) {
1031
range = stmt.range;
1032
if (range[1] - range[0] > 2) {
1033
content = sourceCode.substring(range[0] + 1, range[1] - 1);
1034
if (content[0] === '\n') {
1035
result = ['{'];
1036
}
1037
result.push(content);
1038
}
1039
}
1040
1041
var i, iz, fragment, bodyFlags;
1042
bodyFlags = S_TFFF;
1043
if (flags & F_FUNC_BODY) {
1044
bodyFlags |= F_DIRECTIVE_CTX;
1045
}
1046
1047
for (i = 0, iz = stmt.body.length; i < iz; ++i) {
1048
if (preserveBlankLines) {
1049
// handle spaces before the first line
1050
if (i === 0) {
1051
if (stmt.body[0].leadingComments) {
1052
range = stmt.body[0].leadingComments[0].extendedRange;
1053
content = sourceCode.substring(range[0], range[1]);
1054
if (content[0] === '\n') {
1055
result = ['{'];
1056
}
1057
}
1058
if (!stmt.body[0].leadingComments) {
1059
generateBlankLines(stmt.range[0], stmt.body[0].range[0], result);
1060
}
1061
}
1062
1063
// handle spaces between lines
1064
if (i > 0) {
1065
if (!stmt.body[i - 1].trailingComments && !stmt.body[i].leadingComments) {
1066
generateBlankLines(stmt.body[i - 1].range[1], stmt.body[i].range[0], result);
1067
}
1068
}
1069
}
1070
1071
if (i === iz - 1) {
1072
bodyFlags |= F_SEMICOLON_OPT;
1073
}
1074
1075
if (stmt.body[i].leadingComments && preserveBlankLines) {
1076
fragment = that.generateStatement(stmt.body[i], bodyFlags);
1077
} else {
1078
fragment = addIndent(that.generateStatement(stmt.body[i], bodyFlags));
1079
}
1080
1081
result.push(fragment);
1082
if (!endsWithLineTerminator(toSourceNodeWhenNeeded(fragment).toString())) {
1083
if (preserveBlankLines && i < iz - 1) {
1084
// don't add a new line if there are leading coments
1085
// in the next statement
1086
if (!stmt.body[i + 1].leadingComments) {
1087
result.push(newline);
1088
}
1089
} else {
1090
result.push(newline);
1091
}
1092
}
1093
1094
if (preserveBlankLines) {
1095
// handle spaces after the last line
1096
if (i === iz - 1) {
1097
if (!stmt.body[i].trailingComments) {
1098
generateBlankLines(stmt.body[i].range[1], stmt.range[1], result);
1099
}
1100
}
1101
}
1102
}
1103
});
1104
1105
result.push(addIndent('}'));
1106
return result;
1107
},
1108
1109
BreakStatement: function (stmt, flags) {
1110
if (stmt.label) {
1111
return 'break ' + stmt.label.name + this.semicolon(flags);
1112
}
1113
return 'break' + this.semicolon(flags);
1114
},
1115
1116
ContinueStatement: function (stmt, flags) {
1117
if (stmt.label) {
1118
return 'continue ' + stmt.label.name + this.semicolon(flags);
1119
}
1120
return 'continue' + this.semicolon(flags);
1121
},
1122
1123
ClassBody: function (stmt, flags) {
1124
var result = [ '{', newline], that = this;
1125
1126
withIndent(function (indent) {
1127
var i, iz;
1128
1129
for (i = 0, iz = stmt.body.length; i < iz; ++i) {
1130
result.push(indent);
1131
result.push(that.generateExpression(stmt.body[i], Precedence.Sequence, E_TTT));
1132
if (i + 1 < iz) {
1133
result.push(newline);
1134
}
1135
}
1136
});
1137
1138
if (!endsWithLineTerminator(toSourceNodeWhenNeeded(result).toString())) {
1139
result.push(newline);
1140
}
1141
result.push(base);
1142
result.push('}');
1143
return result;
1144
},
1145
1146
ClassDeclaration: function (stmt, flags) {
1147
var result, fragment;
1148
result = ['class ' + stmt.id.name];
1149
if (stmt.superClass) {
1150
fragment = join('extends', this.generateExpression(stmt.superClass, Precedence.Assignment, E_TTT));
1151
result = join(result, fragment);
1152
}
1153
result.push(space);
1154
result.push(this.generateStatement(stmt.body, S_TFFT));
1155
return result;
1156
},
1157
1158
DirectiveStatement: function (stmt, flags) {
1159
if (extra.raw && stmt.raw) {
1160
return stmt.raw + this.semicolon(flags);
1161
}
1162
return escapeDirective(stmt.directive) + this.semicolon(flags);
1163
},
1164
1165
DoWhileStatement: function (stmt, flags) {
1166
// Because `do 42 while (cond)` is Syntax Error. We need semicolon.
1167
var result = join('do', this.maybeBlock(stmt.body, S_TFFF));
1168
result = this.maybeBlockSuffix(stmt.body, result);
1169
return join(result, [
1170
'while' + space + '(',
1171
this.generateExpression(stmt.test, Precedence.Sequence, E_TTT),
1172
')' + this.semicolon(flags)
1173
]);
1174
},
1175
1176
CatchClause: function (stmt, flags) {
1177
var result, that = this;
1178
withIndent(function () {
1179
var guard;
1180
1181
result = [
1182
'catch' + space + '(',
1183
that.generateExpression(stmt.param, Precedence.Sequence, E_TTT),
1184
')'
1185
];
1186
1187
if (stmt.guard) {
1188
guard = that.generateExpression(stmt.guard, Precedence.Sequence, E_TTT);
1189
result.splice(2, 0, ' if ', guard);
1190
}
1191
});
1192
result.push(this.maybeBlock(stmt.body, S_TFFF));
1193
return result;
1194
},
1195
1196
DebuggerStatement: function (stmt, flags) {
1197
return 'debugger' + this.semicolon(flags);
1198
},
1199
1200
EmptyStatement: function (stmt, flags) {
1201
return ';';
1202
},
1203
1204
ExportDeclaration: function (stmt, flags) {
1205
var result = [ 'export' ], bodyFlags, that = this;
1206
1207
bodyFlags = (flags & F_SEMICOLON_OPT) ? S_TFFT : S_TFFF;
1208
1209
// export default HoistableDeclaration[Default]
1210
// export default AssignmentExpression[In] ;
1211
if (stmt['default']) {
1212
result = join(result, 'default');
1213
if (isStatement(stmt.declaration)) {
1214
result = join(result, this.generateStatement(stmt.declaration, bodyFlags));
1215
} else {
1216
result = join(result, this.generateExpression(stmt.declaration, Precedence.Assignment, E_TTT) + this.semicolon(flags));
1217
}
1218
return result;
1219
}
1220
1221
// export VariableStatement
1222
// export Declaration[Default]
1223
if (stmt.declaration) {
1224
return join(result, this.generateStatement(stmt.declaration, bodyFlags));
1225
}
1226
1227
// export * FromClause ;
1228
// export ExportClause[NoReference] FromClause ;
1229
// export ExportClause ;
1230
if (stmt.specifiers) {
1231
if (stmt.specifiers.length === 0) {
1232
result = join(result, '{' + space + '}');
1233
} else if (stmt.specifiers[0].type === Syntax.ExportBatchSpecifier) {
1234
result = join(result, this.generateExpression(stmt.specifiers[0], Precedence.Sequence, E_TTT));
1235
} else {
1236
result = join(result, '{');
1237
withIndent(function (indent) {
1238
var i, iz;
1239
result.push(newline);
1240
for (i = 0, iz = stmt.specifiers.length; i < iz; ++i) {
1241
result.push(indent);
1242
result.push(that.generateExpression(stmt.specifiers[i], Precedence.Sequence, E_TTT));
1243
if (i + 1 < iz) {
1244
result.push(',' + newline);
1245
}
1246
}
1247
});
1248
if (!endsWithLineTerminator(toSourceNodeWhenNeeded(result).toString())) {
1249
result.push(newline);
1250
}
1251
result.push(base + '}');
1252
}
1253
1254
if (stmt.source) {
1255
result = join(result, [
1256
'from' + space,
1257
// ModuleSpecifier
1258
this.generateExpression(stmt.source, Precedence.Sequence, E_TTT),
1259
this.semicolon(flags)
1260
]);
1261
} else {
1262
result.push(this.semicolon(flags));
1263
}
1264
}
1265
return result;
1266
},
1267
1268
ExpressionStatement: function (stmt, flags) {
1269
var result, fragment;
1270
1271
function isClassPrefixed(fragment) {
1272
var code;
1273
if (fragment.slice(0, 5) !== 'class') {
1274
return false;
1275
}
1276
code = fragment.charCodeAt(5);
1277
return code === 0x7B /* '{' */ || esutils.code.isWhiteSpace(code) || esutils.code.isLineTerminator(code);
1278
}
1279
1280
function isFunctionPrefixed(fragment) {
1281
var code;
1282
if (fragment.slice(0, 8) !== 'function') {
1283
return false;
1284
}
1285
code = fragment.charCodeAt(8);
1286
return code === 0x28 /* '(' */ || esutils.code.isWhiteSpace(code) || code === 0x2A /* '*' */ || esutils.code.isLineTerminator(code);
1287
}
1288
1289
function isAsyncPrefixed(fragment) {
1290
var code, i, iz;
1291
if (fragment.slice(0, 5) !== 'async') {
1292
return false;
1293
}
1294
if (!esutils.code.isWhiteSpace(fragment.charCodeAt(5))) {
1295
return false;
1296
}
1297
for (i = 6, iz = fragment.length; i < iz; ++i) {
1298
if (!esutils.code.isWhiteSpace(fragment.charCodeAt(i))) {
1299
break;
1300
}
1301
}
1302
if (i === iz) {
1303
return false;
1304
}
1305
if (fragment.slice(i, i + 8) !== 'function') {
1306
return false;
1307
}
1308
code = fragment.charCodeAt(i + 8);
1309
return code === 0x28 /* '(' */ || esutils.code.isWhiteSpace(code) || code === 0x2A /* '*' */ || esutils.code.isLineTerminator(code);
1310
}
1311
1312
result = [this.generateExpression(stmt.expression, Precedence.Sequence, E_TTT)];
1313
// 12.4 '{', 'function', 'class' is not allowed in this position.
1314
// wrap expression with parentheses
1315
fragment = toSourceNodeWhenNeeded(result).toString();
1316
if (fragment.charCodeAt(0) === 0x7B /* '{' */ || // ObjectExpression
1317
isClassPrefixed(fragment) ||
1318
isFunctionPrefixed(fragment) ||
1319
isAsyncPrefixed(fragment) ||
1320
(directive && (flags & F_DIRECTIVE_CTX) && stmt.expression.type === Syntax.Literal && typeof stmt.expression.value === 'string')) {
1321
result = ['(', result, ')' + this.semicolon(flags)];
1322
} else {
1323
result.push(this.semicolon(flags));
1324
}
1325
return result;
1326
},
1327
1328
ImportDeclaration: function (stmt, flags) {
1329
// ES6: 15.2.1 valid import declarations:
1330
// - import ImportClause FromClause ;
1331
// - import ModuleSpecifier ;
1332
var result, cursor, that = this;
1333
1334
// If no ImportClause is present,
1335
// this should be `import ModuleSpecifier` so skip `from`
1336
// ModuleSpecifier is StringLiteral.
1337
if (stmt.specifiers.length === 0) {
1338
// import ModuleSpecifier ;
1339
return [
1340
'import',
1341
space,
1342
// ModuleSpecifier
1343
this.generateExpression(stmt.source, Precedence.Sequence, E_TTT),
1344
this.semicolon(flags)
1345
];
1346
}
1347
1348
// import ImportClause FromClause ;
1349
result = [
1350
'import'
1351
];
1352
cursor = 0;
1353
1354
// ImportedBinding
1355
if (stmt.specifiers[cursor].type === Syntax.ImportDefaultSpecifier) {
1356
result = join(result, [
1357
this.generateExpression(stmt.specifiers[cursor], Precedence.Sequence, E_TTT)
1358
]);
1359
++cursor;
1360
}
1361
1362
if (stmt.specifiers[cursor]) {
1363
if (cursor !== 0) {
1364
result.push(',');
1365
}
1366
1367
if (stmt.specifiers[cursor].type === Syntax.ImportNamespaceSpecifier) {
1368
// NameSpaceImport
1369
result = join(result, [
1370
space,
1371
this.generateExpression(stmt.specifiers[cursor], Precedence.Sequence, E_TTT)
1372
]);
1373
} else {
1374
// NamedImports
1375
result.push(space + '{');
1376
1377
if ((stmt.specifiers.length - cursor) === 1) {
1378
// import { ... } from "...";
1379
result.push(space);
1380
result.push(this.generateExpression(stmt.specifiers[cursor], Precedence.Sequence, E_TTT));
1381
result.push(space + '}' + space);
1382
} else {
1383
// import {
1384
// ...,
1385
// ...,
1386
// } from "...";
1387
withIndent(function (indent) {
1388
var i, iz;
1389
result.push(newline);
1390
for (i = cursor, iz = stmt.specifiers.length; i < iz; ++i) {
1391
result.push(indent);
1392
result.push(that.generateExpression(stmt.specifiers[i], Precedence.Sequence, E_TTT));
1393
if (i + 1 < iz) {
1394
result.push(',' + newline);
1395
}
1396
}
1397
});
1398
if (!endsWithLineTerminator(toSourceNodeWhenNeeded(result).toString())) {
1399
result.push(newline);
1400
}
1401
result.push(base + '}' + space);
1402
}
1403
}
1404
}
1405
1406
result = join(result, [
1407
'from' + space,
1408
// ModuleSpecifier
1409
this.generateExpression(stmt.source, Precedence.Sequence, E_TTT),
1410
this.semicolon(flags)
1411
]);
1412
return result;
1413
},
1414
1415
VariableDeclarator: function (stmt, flags) {
1416
var itemFlags = (flags & F_ALLOW_IN) ? E_TTT : E_FTT;
1417
if (stmt.init) {
1418
return [
1419
this.generateExpression(stmt.id, Precedence.Assignment, itemFlags),
1420
space,
1421
'=',
1422
space,
1423
this.generateExpression(stmt.init, Precedence.Assignment, itemFlags)
1424
];
1425
}
1426
return this.generatePattern(stmt.id, Precedence.Assignment, itemFlags);
1427
},
1428
1429
VariableDeclaration: function (stmt, flags) {
1430
// VariableDeclarator is typed as Statement,
1431
// but joined with comma (not LineTerminator).
1432
// So if comment is attached to target node, we should specialize.
1433
var result, i, iz, node, bodyFlags, that = this;
1434
1435
result = [ stmt.kind ];
1436
1437
bodyFlags = (flags & F_ALLOW_IN) ? S_TFFF : S_FFFF;
1438
1439
function block() {
1440
node = stmt.declarations[0];
1441
if (extra.comment && node.leadingComments) {
1442
result.push('\n');
1443
result.push(addIndent(that.generateStatement(node, bodyFlags)));
1444
} else {
1445
result.push(noEmptySpace());
1446
result.push(that.generateStatement(node, bodyFlags));
1447
}
1448
1449
for (i = 1, iz = stmt.declarations.length; i < iz; ++i) {
1450
node = stmt.declarations[i];
1451
if (extra.comment && node.leadingComments) {
1452
result.push(',' + newline);
1453
result.push(addIndent(that.generateStatement(node, bodyFlags)));
1454
} else {
1455
result.push(',' + space);
1456
result.push(that.generateStatement(node, bodyFlags));
1457
}
1458
}
1459
}
1460
1461
if (stmt.declarations.length > 1) {
1462
withIndent(block);
1463
} else {
1464
block();
1465
}
1466
1467
result.push(this.semicolon(flags));
1468
1469
return result;
1470
},
1471
1472
ThrowStatement: function (stmt, flags) {
1473
return [join(
1474
'throw',
1475
this.generateExpression(stmt.argument, Precedence.Sequence, E_TTT)
1476
), this.semicolon(flags)];
1477
},
1478
1479
TryStatement: function (stmt, flags) {
1480
var result, i, iz, guardedHandlers;
1481
1482
result = ['try', this.maybeBlock(stmt.block, S_TFFF)];
1483
result = this.maybeBlockSuffix(stmt.block, result);
1484
1485
if (stmt.handlers) {
1486
// old interface
1487
for (i = 0, iz = stmt.handlers.length; i < iz; ++i) {
1488
result = join(result, this.generateStatement(stmt.handlers[i], S_TFFF));
1489
if (stmt.finalizer || i + 1 !== iz) {
1490
result = this.maybeBlockSuffix(stmt.handlers[i].body, result);
1491
}
1492
}
1493
} else {
1494
guardedHandlers = stmt.guardedHandlers || [];
1495
1496
for (i = 0, iz = guardedHandlers.length; i < iz; ++i) {
1497
result = join(result, this.generateStatement(guardedHandlers[i], S_TFFF));
1498
if (stmt.finalizer || i + 1 !== iz) {
1499
result = this.maybeBlockSuffix(guardedHandlers[i].body, result);
1500
}
1501
}
1502
1503
// new interface
1504
if (stmt.handler) {
1505
if (isArray(stmt.handler)) {
1506
for (i = 0, iz = stmt.handler.length; i < iz; ++i) {
1507
result = join(result, this.generateStatement(stmt.handler[i], S_TFFF));
1508
if (stmt.finalizer || i + 1 !== iz) {
1509
result = this.maybeBlockSuffix(stmt.handler[i].body, result);
1510
}
1511
}
1512
} else {
1513
result = join(result, this.generateStatement(stmt.handler, S_TFFF));
1514
if (stmt.finalizer) {
1515
result = this.maybeBlockSuffix(stmt.handler.body, result);
1516
}
1517
}
1518
}
1519
}
1520
if (stmt.finalizer) {
1521
result = join(result, ['finally', this.maybeBlock(stmt.finalizer, S_TFFF)]);
1522
}
1523
return result;
1524
},
1525
1526
SwitchStatement: function (stmt, flags) {
1527
var result, fragment, i, iz, bodyFlags, that = this;
1528
withIndent(function () {
1529
result = [
1530
'switch' + space + '(',
1531
that.generateExpression(stmt.discriminant, Precedence.Sequence, E_TTT),
1532
')' + space + '{' + newline
1533
];
1534
});
1535
if (stmt.cases) {
1536
bodyFlags = S_TFFF;
1537
for (i = 0, iz = stmt.cases.length; i < iz; ++i) {
1538
if (i === iz - 1) {
1539
bodyFlags |= F_SEMICOLON_OPT;
1540
}
1541
fragment = addIndent(this.generateStatement(stmt.cases[i], bodyFlags));
1542
result.push(fragment);
1543
if (!endsWithLineTerminator(toSourceNodeWhenNeeded(fragment).toString())) {
1544
result.push(newline);
1545
}
1546
}
1547
}
1548
result.push(addIndent('}'));
1549
return result;
1550
},
1551
1552
SwitchCase: function (stmt, flags) {
1553
var result, fragment, i, iz, bodyFlags, that = this;
1554
withIndent(function () {
1555
if (stmt.test) {
1556
result = [
1557
join('case', that.generateExpression(stmt.test, Precedence.Sequence, E_TTT)),
1558
':'
1559
];
1560
} else {
1561
result = ['default:'];
1562
}
1563
1564
i = 0;
1565
iz = stmt.consequent.length;
1566
if (iz && stmt.consequent[0].type === Syntax.BlockStatement) {
1567
fragment = that.maybeBlock(stmt.consequent[0], S_TFFF);
1568
result.push(fragment);
1569
i = 1;
1570
}
1571
1572
if (i !== iz && !endsWithLineTerminator(toSourceNodeWhenNeeded(result).toString())) {
1573
result.push(newline);
1574
}
1575
1576
bodyFlags = S_TFFF;
1577
for (; i < iz; ++i) {
1578
if (i === iz - 1 && flags & F_SEMICOLON_OPT) {
1579
bodyFlags |= F_SEMICOLON_OPT;
1580
}
1581
fragment = addIndent(that.generateStatement(stmt.consequent[i], bodyFlags));
1582
result.push(fragment);
1583
if (i + 1 !== iz && !endsWithLineTerminator(toSourceNodeWhenNeeded(fragment).toString())) {
1584
result.push(newline);
1585
}
1586
}
1587
});
1588
return result;
1589
},
1590
1591
IfStatement: function (stmt, flags) {
1592
var result, bodyFlags, semicolonOptional, that = this;
1593
withIndent(function () {
1594
result = [
1595
'if' + space + '(',
1596
that.generateExpression(stmt.test, Precedence.Sequence, E_TTT),
1597
')'
1598
];
1599
});
1600
semicolonOptional = flags & F_SEMICOLON_OPT;
1601
bodyFlags = S_TFFF;
1602
if (semicolonOptional) {
1603
bodyFlags |= F_SEMICOLON_OPT;
1604
}
1605
if (stmt.alternate) {
1606
result.push(this.maybeBlock(stmt.consequent, S_TFFF));
1607
result = this.maybeBlockSuffix(stmt.consequent, result);
1608
if (stmt.alternate.type === Syntax.IfStatement) {
1609
result = join(result, ['else ', this.generateStatement(stmt.alternate, bodyFlags)]);
1610
} else {
1611
result = join(result, join('else', this.maybeBlock(stmt.alternate, bodyFlags)));
1612
}
1613
} else {
1614
result.push(this.maybeBlock(stmt.consequent, bodyFlags));
1615
}
1616
return result;
1617
},
1618
1619
ForStatement: function (stmt, flags) {
1620
var result, that = this;
1621
withIndent(function () {
1622
result = ['for' + space + '('];
1623
if (stmt.init) {
1624
if (stmt.init.type === Syntax.VariableDeclaration) {
1625
result.push(that.generateStatement(stmt.init, S_FFFF));
1626
} else {
1627
// F_ALLOW_IN becomes false.
1628
result.push(that.generateExpression(stmt.init, Precedence.Sequence, E_FTT));
1629
result.push(';');
1630
}
1631
} else {
1632
result.push(';');
1633
}
1634
1635
if (stmt.test) {
1636
result.push(space);
1637
result.push(that.generateExpression(stmt.test, Precedence.Sequence, E_TTT));
1638
result.push(';');
1639
} else {
1640
result.push(';');
1641
}
1642
1643
if (stmt.update) {
1644
result.push(space);
1645
result.push(that.generateExpression(stmt.update, Precedence.Sequence, E_TTT));
1646
result.push(')');
1647
} else {
1648
result.push(')');
1649
}
1650
});
1651
1652
result.push(this.maybeBlock(stmt.body, flags & F_SEMICOLON_OPT ? S_TFFT : S_TFFF));
1653
return result;
1654
},
1655
1656
ForInStatement: function (stmt, flags) {
1657
return this.generateIterationForStatement('in', stmt, flags & F_SEMICOLON_OPT ? S_TFFT : S_TFFF);
1658
},
1659
1660
ForOfStatement: function (stmt, flags) {
1661
return this.generateIterationForStatement('of', stmt, flags & F_SEMICOLON_OPT ? S_TFFT : S_TFFF);
1662
},
1663
1664
LabeledStatement: function (stmt, flags) {
1665
return [stmt.label.name + ':', this.maybeBlock(stmt.body, flags & F_SEMICOLON_OPT ? S_TFFT : S_TFFF)];
1666
},
1667
1668
Program: function (stmt, flags) {
1669
var result, fragment, i, iz, bodyFlags;
1670
iz = stmt.body.length;
1671
result = [safeConcatenation && iz > 0 ? '\n' : ''];
1672
bodyFlags = S_TFTF;
1673
for (i = 0; i < iz; ++i) {
1674
if (!safeConcatenation && i === iz - 1) {
1675
bodyFlags |= F_SEMICOLON_OPT;
1676
}
1677
1678
if (preserveBlankLines) {
1679
// handle spaces before the first line
1680
if (i === 0) {
1681
if (!stmt.body[0].leadingComments) {
1682
generateBlankLines(stmt.range[0], stmt.body[i].range[0], result);
1683
}
1684
}
1685
1686
// handle spaces between lines
1687
if (i > 0) {
1688
if (!stmt.body[i - 1].trailingComments && !stmt.body[i].leadingComments) {
1689
generateBlankLines(stmt.body[i - 1].range[1], stmt.body[i].range[0], result);
1690
}
1691
}
1692
}
1693
1694
fragment = addIndent(this.generateStatement(stmt.body[i], bodyFlags));
1695
result.push(fragment);
1696
if (i + 1 < iz && !endsWithLineTerminator(toSourceNodeWhenNeeded(fragment).toString())) {
1697
if (preserveBlankLines) {
1698
if (!stmt.body[i + 1].leadingComments) {
1699
result.push(newline);
1700
}
1701
} else {
1702
result.push(newline);
1703
}
1704
}
1705
1706
if (preserveBlankLines) {
1707
// handle spaces after the last line
1708
if (i === iz - 1) {
1709
if (!stmt.body[i].trailingComments) {
1710
generateBlankLines(stmt.body[i].range[1], stmt.range[1], result);
1711
}
1712
}
1713
}
1714
}
1715
return result;
1716
},
1717
1718
FunctionDeclaration: function (stmt, flags) {
1719
return [
1720
generateAsyncPrefix(stmt, true),
1721
'function',
1722
generateStarSuffix(stmt) || noEmptySpace(),
1723
generateIdentifier(stmt.id),
1724
this.generateFunctionBody(stmt)
1725
];
1726
},
1727
1728
ReturnStatement: function (stmt, flags) {
1729
if (stmt.argument) {
1730
return [join(
1731
'return',
1732
this.generateExpression(stmt.argument, Precedence.Sequence, E_TTT)
1733
), this.semicolon(flags)];
1734
}
1735
return ['return' + this.semicolon(flags)];
1736
},
1737
1738
WhileStatement: function (stmt, flags) {
1739
var result, that = this;
1740
withIndent(function () {
1741
result = [
1742
'while' + space + '(',
1743
that.generateExpression(stmt.test, Precedence.Sequence, E_TTT),
1744
')'
1745
];
1746
});
1747
result.push(this.maybeBlock(stmt.body, flags & F_SEMICOLON_OPT ? S_TFFT : S_TFFF));
1748
return result;
1749
},
1750
1751
WithStatement: function (stmt, flags) {
1752
var result, that = this;
1753
withIndent(function () {
1754
result = [
1755
'with' + space + '(',
1756
that.generateExpression(stmt.object, Precedence.Sequence, E_TTT),
1757
')'
1758
];
1759
});
1760
result.push(this.maybeBlock(stmt.body, flags & F_SEMICOLON_OPT ? S_TFFT : S_TFFF));
1761
return result;
1762
}
1763
1764
};
1765
1766
merge(CodeGenerator.prototype, CodeGenerator.Statement);
1767
1768
// Expressions.
1769
1770
CodeGenerator.Expression = {
1771
1772
SequenceExpression: function (expr, precedence, flags) {
1773
var result, i, iz;
1774
if (Precedence.Sequence < precedence) {
1775
flags |= F_ALLOW_IN;
1776
}
1777
result = [];
1778
for (i = 0, iz = expr.expressions.length; i < iz; ++i) {
1779
result.push(this.generateExpression(expr.expressions[i], Precedence.Assignment, flags));
1780
if (i + 1 < iz) {
1781
result.push(',' + space);
1782
}
1783
}
1784
return parenthesize(result, Precedence.Sequence, precedence);
1785
},
1786
1787
AssignmentExpression: function (expr, precedence, flags) {
1788
return this.generateAssignment(expr.left, expr.right, expr.operator, precedence, flags);
1789
},
1790
1791
ArrowFunctionExpression: function (expr, precedence, flags) {
1792
return parenthesize(this.generateFunctionBody(expr), Precedence.ArrowFunction, precedence);
1793
},
1794
1795
ConditionalExpression: function (expr, precedence, flags) {
1796
if (Precedence.Conditional < precedence) {
1797
flags |= F_ALLOW_IN;
1798
}
1799
return parenthesize(
1800
[
1801
this.generateExpression(expr.test, Precedence.LogicalOR, flags),
1802
space + '?' + space,
1803
this.generateExpression(expr.consequent, Precedence.Assignment, flags),
1804
space + ':' + space,
1805
this.generateExpression(expr.alternate, Precedence.Assignment, flags)
1806
],
1807
Precedence.Conditional,
1808
precedence
1809
);
1810
},
1811
1812
LogicalExpression: function (expr, precedence, flags) {
1813
return this.BinaryExpression(expr, precedence, flags);
1814
},
1815
1816
BinaryExpression: function (expr, precedence, flags) {
1817
var result, currentPrecedence, fragment, leftSource;
1818
currentPrecedence = BinaryPrecedence[expr.operator];
1819
1820
if (currentPrecedence < precedence) {
1821
flags |= F_ALLOW_IN;
1822
}
1823
1824
fragment = this.generateExpression(expr.left, currentPrecedence, flags);
1825
1826
leftSource = fragment.toString();
1827
1828
if (leftSource.charCodeAt(leftSource.length - 1) === 0x2F /* / */ && esutils.code.isIdentifierPart(expr.operator.charCodeAt(0))) {
1829
result = [fragment, noEmptySpace(), expr.operator];
1830
} else {
1831
result = join(fragment, expr.operator);
1832
}
1833
1834
fragment = this.generateExpression(expr.right, currentPrecedence + 1, flags);
1835
1836
if (expr.operator === '/' && fragment.toString().charAt(0) === '/' ||
1837
expr.operator.slice(-1) === '<' && fragment.toString().slice(0, 3) === '!--') {
1838
// If '/' concats with '/' or `<` concats with `!--`, it is interpreted as comment start
1839
result.push(noEmptySpace());
1840
result.push(fragment);
1841
} else {
1842
result = join(result, fragment);
1843
}
1844
1845
if (expr.operator === 'in' && !(flags & F_ALLOW_IN)) {
1846
return ['(', result, ')'];
1847
}
1848
return parenthesize(result, currentPrecedence, precedence);
1849
},
1850
1851
CallExpression: function (expr, precedence, flags) {
1852
var result, i, iz;
1853
// F_ALLOW_UNPARATH_NEW becomes false.
1854
result = [this.generateExpression(expr.callee, Precedence.Call, E_TTF)];
1855
result.push('(');
1856
for (i = 0, iz = expr['arguments'].length; i < iz; ++i) {
1857
result.push(this.generateExpression(expr['arguments'][i], Precedence.Assignment, E_TTT));
1858
if (i + 1 < iz) {
1859
result.push(',' + space);
1860
}
1861
}
1862
result.push(')');
1863
1864
if (!(flags & F_ALLOW_CALL)) {
1865
return ['(', result, ')'];
1866
}
1867
return parenthesize(result, Precedence.Call, precedence);
1868
},
1869
1870
NewExpression: function (expr, precedence, flags) {
1871
var result, length, i, iz, itemFlags;
1872
length = expr['arguments'].length;
1873
1874
// F_ALLOW_CALL becomes false.
1875
// F_ALLOW_UNPARATH_NEW may become false.
1876
itemFlags = (flags & F_ALLOW_UNPARATH_NEW && !parentheses && length === 0) ? E_TFT : E_TFF;
1877
1878
result = join(
1879
'new',
1880
this.generateExpression(expr.callee, Precedence.New, itemFlags)
1881
);
1882
1883
if (!(flags & F_ALLOW_UNPARATH_NEW) || parentheses || length > 0) {
1884
result.push('(');
1885
for (i = 0, iz = length; i < iz; ++i) {
1886
result.push(this.generateExpression(expr['arguments'][i], Precedence.Assignment, E_TTT));
1887
if (i + 1 < iz) {
1888
result.push(',' + space);
1889
}
1890
}
1891
result.push(')');
1892
}
1893
1894
return parenthesize(result, Precedence.New, precedence);
1895
},
1896
1897
MemberExpression: function (expr, precedence, flags) {
1898
var result, fragment;
1899
1900
// F_ALLOW_UNPARATH_NEW becomes false.
1901
result = [this.generateExpression(expr.object, Precedence.Call, (flags & F_ALLOW_CALL) ? E_TTF : E_TFF)];
1902
1903
if (expr.computed) {
1904
result.push('[');
1905
result.push(this.generateExpression(expr.property, Precedence.Sequence, flags & F_ALLOW_CALL ? E_TTT : E_TFT));
1906
result.push(']');
1907
} else {
1908
if (expr.object.type === Syntax.Literal && typeof expr.object.value === 'number') {
1909
fragment = toSourceNodeWhenNeeded(result).toString();
1910
// When the following conditions are all true,
1911
// 1. No floating point
1912
// 2. Don't have exponents
1913
// 3. The last character is a decimal digit
1914
// 4. Not hexadecimal OR octal number literal
1915
// we should add a floating point.
1916
if (
1917
fragment.indexOf('.') < 0 &&
1918
!/[eExX]/.test(fragment) &&
1919
esutils.code.isDecimalDigit(fragment.charCodeAt(fragment.length - 1)) &&
1920
!(fragment.length >= 2 && fragment.charCodeAt(0) === 48) // '0'
1921
) {
1922
result.push('.');
1923
}
1924
}
1925
result.push('.');
1926
result.push(generateIdentifier(expr.property));
1927
}
1928
1929
return parenthesize(result, Precedence.Member, precedence);
1930
},
1931
1932
UnaryExpression: function (expr, precedence, flags) {
1933
var result, fragment, rightCharCode, leftSource, leftCharCode;
1934
fragment = this.generateExpression(expr.argument, Precedence.Unary, E_TTT);
1935
1936
if (space === '') {
1937
result = join(expr.operator, fragment);
1938
} else {
1939
result = [expr.operator];
1940
if (expr.operator.length > 2) {
1941
// delete, void, typeof
1942
// get `typeof []`, not `typeof[]`
1943
result = join(result, fragment);
1944
} else {
1945
// Prevent inserting spaces between operator and argument if it is unnecessary
1946
// like, `!cond`
1947
leftSource = toSourceNodeWhenNeeded(result).toString();
1948
leftCharCode = leftSource.charCodeAt(leftSource.length - 1);
1949
rightCharCode = fragment.toString().charCodeAt(0);
1950
1951
if (((leftCharCode === 0x2B /* + */ || leftCharCode === 0x2D /* - */) && leftCharCode === rightCharCode) ||
1952
(esutils.code.isIdentifierPart(leftCharCode) && esutils.code.isIdentifierPart(rightCharCode))) {
1953
result.push(noEmptySpace());
1954
result.push(fragment);
1955
} else {
1956
result.push(fragment);
1957
}
1958
}
1959
}
1960
return parenthesize(result, Precedence.Unary, precedence);
1961
},
1962
1963
YieldExpression: function (expr, precedence, flags) {
1964
var result;
1965
if (expr.delegate) {
1966
result = 'yield*';
1967
} else {
1968
result = 'yield';
1969
}
1970
if (expr.argument) {
1971
result = join(
1972
result,
1973
this.generateExpression(expr.argument, Precedence.Yield, E_TTT)
1974
);
1975
}
1976
return parenthesize(result, Precedence.Yield, precedence);
1977
},
1978
1979
AwaitExpression: function (expr, precedence, flags) {
1980
var result = join(
1981
expr.delegate ? 'await*' : 'await',
1982
this.generateExpression(expr.argument, Precedence.Await, E_TTT)
1983
);
1984
return parenthesize(result, Precedence.Await, precedence);
1985
},
1986
1987
UpdateExpression: function (expr, precedence, flags) {
1988
if (expr.prefix) {
1989
return parenthesize(
1990
[
1991
expr.operator,
1992
this.generateExpression(expr.argument, Precedence.Unary, E_TTT)
1993
],
1994
Precedence.Unary,
1995
precedence
1996
);
1997
}
1998
return parenthesize(
1999
[
2000
this.generateExpression(expr.argument, Precedence.Postfix, E_TTT),
2001
expr.operator
2002
],
2003
Precedence.Postfix,
2004
precedence
2005
);
2006
},
2007
2008
FunctionExpression: function (expr, precedence, flags) {
2009
var result = [
2010
generateAsyncPrefix(expr, true),
2011
'function'
2012
];
2013
if (expr.id) {
2014
result.push(generateStarSuffix(expr) || noEmptySpace());
2015
result.push(generateIdentifier(expr.id));
2016
} else {
2017
result.push(generateStarSuffix(expr) || space);
2018
}
2019
result.push(this.generateFunctionBody(expr));
2020
return result;
2021
},
2022
2023
ExportBatchSpecifier: function (expr, precedence, flags) {
2024
return '*';
2025
},
2026
2027
ArrayPattern: function (expr, precedence, flags) {
2028
return this.ArrayExpression(expr, precedence, flags);
2029
},
2030
2031
ArrayExpression: function (expr, precedence, flags) {
2032
var result, multiline, that = this;
2033
if (!expr.elements.length) {
2034
return '[]';
2035
}
2036
multiline = expr.elements.length > 1;
2037
result = ['[', multiline ? newline : ''];
2038
withIndent(function (indent) {
2039
var i, iz;
2040
for (i = 0, iz = expr.elements.length; i < iz; ++i) {
2041
if (!expr.elements[i]) {
2042
if (multiline) {
2043
result.push(indent);
2044
}
2045
if (i + 1 === iz) {
2046
result.push(',');
2047
}
2048
} else {
2049
result.push(multiline ? indent : '');
2050
result.push(that.generateExpression(expr.elements[i], Precedence.Assignment, E_TTT));
2051
}
2052
if (i + 1 < iz) {
2053
result.push(',' + (multiline ? newline : space));
2054
}
2055
}
2056
});
2057
if (multiline && !endsWithLineTerminator(toSourceNodeWhenNeeded(result).toString())) {
2058
result.push(newline);
2059
}
2060
result.push(multiline ? base : '');
2061
result.push(']');
2062
return result;
2063
},
2064
2065
ClassExpression: function (expr, precedence, flags) {
2066
var result, fragment;
2067
result = ['class'];
2068
if (expr.id) {
2069
result = join(result, this.generateExpression(expr.id, Precedence.Sequence, E_TTT));
2070
}
2071
if (expr.superClass) {
2072
fragment = join('extends', this.generateExpression(expr.superClass, Precedence.Assignment, E_TTT));
2073
result = join(result, fragment);
2074
}
2075
result.push(space);
2076
result.push(this.generateStatement(expr.body, S_TFFT));
2077
return result;
2078
},
2079
2080
MethodDefinition: function (expr, precedence, flags) {
2081
var result, fragment;
2082
if (expr['static']) {
2083
result = ['static' + space];
2084
} else {
2085
result = [];
2086
}
2087
if (expr.kind === 'get' || expr.kind === 'set') {
2088
fragment = [
2089
join(expr.kind, this.generatePropertyKey(expr.key, expr.computed)),
2090
this.generateFunctionBody(expr.value)
2091
];
2092
} else {
2093
fragment = [
2094
generateMethodPrefix(expr),
2095
this.generatePropertyKey(expr.key, expr.computed),
2096
this.generateFunctionBody(expr.value)
2097
];
2098
}
2099
return join(result, fragment);
2100
},
2101
2102
Property: function (expr, precedence, flags) {
2103
if (expr.kind === 'get' || expr.kind === 'set') {
2104
return [
2105
expr.kind, noEmptySpace(),
2106
this.generatePropertyKey(expr.key, expr.computed),
2107
this.generateFunctionBody(expr.value)
2108
];
2109
}
2110
2111
if (expr.shorthand) {
2112
return this.generatePropertyKey(expr.key, expr.computed);
2113
}
2114
2115
if (expr.method) {
2116
return [
2117
generateMethodPrefix(expr),
2118
this.generatePropertyKey(expr.key, expr.computed),
2119
this.generateFunctionBody(expr.value)
2120
];
2121
}
2122
2123
return [
2124
this.generatePropertyKey(expr.key, expr.computed),
2125
':' + space,
2126
this.generateExpression(expr.value, Precedence.Assignment, E_TTT)
2127
];
2128
},
2129
2130
ObjectExpression: function (expr, precedence, flags) {
2131
var multiline, result, fragment, that = this;
2132
2133
if (!expr.properties.length) {
2134
return '{}';
2135
}
2136
multiline = expr.properties.length > 1;
2137
2138
withIndent(function () {
2139
fragment = that.generateExpression(expr.properties[0], Precedence.Sequence, E_TTT);
2140
});
2141
2142
if (!multiline) {
2143
// issues 4
2144
// Do not transform from
2145
// dejavu.Class.declare({
2146
// method2: function () {}
2147
// });
2148
// to
2149
// dejavu.Class.declare({method2: function () {
2150
// }});
2151
if (!hasLineTerminator(toSourceNodeWhenNeeded(fragment).toString())) {
2152
return [ '{', space, fragment, space, '}' ];
2153
}
2154
}
2155
2156
withIndent(function (indent) {
2157
var i, iz;
2158
result = [ '{', newline, indent, fragment ];
2159
2160
if (multiline) {
2161
result.push(',' + newline);
2162
for (i = 1, iz = expr.properties.length; i < iz; ++i) {
2163
result.push(indent);
2164
result.push(that.generateExpression(expr.properties[i], Precedence.Sequence, E_TTT));
2165
if (i + 1 < iz) {
2166
result.push(',' + newline);
2167
}
2168
}
2169
}
2170
});
2171
2172
if (!endsWithLineTerminator(toSourceNodeWhenNeeded(result).toString())) {
2173
result.push(newline);
2174
}
2175
result.push(base);
2176
result.push('}');
2177
return result;
2178
},
2179
2180
ObjectPattern: function (expr, precedence, flags) {
2181
var result, i, iz, multiline, property, that = this;
2182
if (!expr.properties.length) {
2183
return '{}';
2184
}
2185
2186
multiline = false;
2187
if (expr.properties.length === 1) {
2188
property = expr.properties[0];
2189
if (property.value.type !== Syntax.Identifier) {
2190
multiline = true;
2191
}
2192
} else {
2193
for (i = 0, iz = expr.properties.length; i < iz; ++i) {
2194
property = expr.properties[i];
2195
if (!property.shorthand) {
2196
multiline = true;
2197
break;
2198
}
2199
}
2200
}
2201
result = ['{', multiline ? newline : '' ];
2202
2203
withIndent(function (indent) {
2204
var i, iz;
2205
for (i = 0, iz = expr.properties.length; i < iz; ++i) {
2206
result.push(multiline ? indent : '');
2207
result.push(that.generateExpression(expr.properties[i], Precedence.Sequence, E_TTT));
2208
if (i + 1 < iz) {
2209
result.push(',' + (multiline ? newline : space));
2210
}
2211
}
2212
});
2213
2214
if (multiline && !endsWithLineTerminator(toSourceNodeWhenNeeded(result).toString())) {
2215
result.push(newline);
2216
}
2217
result.push(multiline ? base : '');
2218
result.push('}');
2219
return result;
2220
},
2221
2222
ThisExpression: function (expr, precedence, flags) {
2223
return 'this';
2224
},
2225
2226
Identifier: function (expr, precedence, flags) {
2227
return generateIdentifier(expr);
2228
},
2229
2230
ImportDefaultSpecifier: function (expr, precedence, flags) {
2231
return generateIdentifier(expr.id);
2232
},
2233
2234
ImportNamespaceSpecifier: function (expr, precedence, flags) {
2235
var result = ['*'];
2236
if (expr.id) {
2237
result.push(space + 'as' + noEmptySpace() + generateIdentifier(expr.id));
2238
}
2239
return result;
2240
},
2241
2242
ImportSpecifier: function (expr, precedence, flags) {
2243
return this.ExportSpecifier(expr, precedence, flags);
2244
},
2245
2246
ExportSpecifier: function (expr, precedence, flags) {
2247
var result = [ expr.id.name ];
2248
if (expr.name) {
2249
result.push(noEmptySpace() + 'as' + noEmptySpace() + generateIdentifier(expr.name));
2250
}
2251
return result;
2252
},
2253
2254
Literal: function (expr, precedence, flags) {
2255
var raw;
2256
if (expr.hasOwnProperty('raw') && parse && extra.raw) {
2257
try {
2258
raw = parse(expr.raw).body[0].expression;
2259
if (raw.type === Syntax.Literal) {
2260
if (raw.value === expr.value) {
2261
return expr.raw;
2262
}
2263
}
2264
} catch (e) {
2265
// not use raw property
2266
}
2267
}
2268
2269
if (expr.value === null) {
2270
return 'null';
2271
}
2272
2273
if (typeof expr.value === 'string') {
2274
return escapeString(expr.value);
2275
}
2276
2277
if (typeof expr.value === 'number') {
2278
return generateNumber(expr.value);
2279
}
2280
2281
if (typeof expr.value === 'boolean') {
2282
return expr.value ? 'true' : 'false';
2283
}
2284
2285
return generateRegExp(expr.value);
2286
},
2287
2288
GeneratorExpression: function (expr, precedence, flags) {
2289
return this.ComprehensionExpression(expr, precedence, flags);
2290
},
2291
2292
ComprehensionExpression: function (expr, precedence, flags) {
2293
// GeneratorExpression should be parenthesized with (...), ComprehensionExpression with [...]
2294
// Due to https://bugzilla.mozilla.org/show_bug.cgi?id=883468 position of expr.body can differ in Spidermonkey and ES6
2295
2296
var result, i, iz, fragment, that = this;
2297
result = (expr.type === Syntax.GeneratorExpression) ? ['('] : ['['];
2298
2299
if (extra.moz.comprehensionExpressionStartsWithAssignment) {
2300
fragment = this.generateExpression(expr.body, Precedence.Assignment, E_TTT);
2301
result.push(fragment);
2302
}
2303
2304
if (expr.blocks) {
2305
withIndent(function () {
2306
for (i = 0, iz = expr.blocks.length; i < iz; ++i) {
2307
fragment = that.generateExpression(expr.blocks[i], Precedence.Sequence, E_TTT);
2308
if (i > 0 || extra.moz.comprehensionExpressionStartsWithAssignment) {
2309
result = join(result, fragment);
2310
} else {
2311
result.push(fragment);
2312
}
2313
}
2314
});
2315
}
2316
2317
if (expr.filter) {
2318
result = join(result, 'if' + space);
2319
fragment = this.generateExpression(expr.filter, Precedence.Sequence, E_TTT);
2320
result = join(result, [ '(', fragment, ')' ]);
2321
}
2322
2323
if (!extra.moz.comprehensionExpressionStartsWithAssignment) {
2324
fragment = this.generateExpression(expr.body, Precedence.Assignment, E_TTT);
2325
2326
result = join(result, fragment);
2327
}
2328
2329
result.push((expr.type === Syntax.GeneratorExpression) ? ')' : ']');
2330
return result;
2331
},
2332
2333
ComprehensionBlock: function (expr, precedence, flags) {
2334
var fragment;
2335
if (expr.left.type === Syntax.VariableDeclaration) {
2336
fragment = [
2337
expr.left.kind, noEmptySpace(),
2338
this.generateStatement(expr.left.declarations[0], S_FFFF)
2339
];
2340
} else {
2341
fragment = this.generateExpression(expr.left, Precedence.Call, E_TTT);
2342
}
2343
2344
fragment = join(fragment, expr.of ? 'of' : 'in');
2345
fragment = join(fragment, this.generateExpression(expr.right, Precedence.Sequence, E_TTT));
2346
2347
return [ 'for' + space + '(', fragment, ')' ];
2348
},
2349
2350
SpreadElement: function (expr, precedence, flags) {
2351
return [
2352
'...',
2353
this.generateExpression(expr.argument, Precedence.Assignment, E_TTT)
2354
];
2355
},
2356
2357
TaggedTemplateExpression: function (expr, precedence, flags) {
2358
var itemFlags = E_TTF;
2359
if (!(flags & F_ALLOW_CALL)) {
2360
itemFlags = E_TFF;
2361
}
2362
var result = [
2363
this.generateExpression(expr.tag, Precedence.Call, itemFlags),
2364
this.generateExpression(expr.quasi, Precedence.Primary, E_FFT)
2365
];
2366
return parenthesize(result, Precedence.TaggedTemplate, precedence);
2367
},
2368
2369
TemplateElement: function (expr, precedence, flags) {
2370
// Don't use "cooked". Since tagged template can use raw template
2371
// representation. So if we do so, it breaks the script semantics.
2372
return expr.value.raw;
2373
},
2374
2375
TemplateLiteral: function (expr, precedence, flags) {
2376
var result, i, iz;
2377
result = [ '`' ];
2378
for (i = 0, iz = expr.quasis.length; i < iz; ++i) {
2379
result.push(this.generateExpression(expr.quasis[i], Precedence.Primary, E_TTT));
2380
if (i + 1 < iz) {
2381
result.push('${' + space);
2382
result.push(this.generateExpression(expr.expressions[i], Precedence.Sequence, E_TTT));
2383
result.push(space + '}');
2384
}
2385
}
2386
result.push('`');
2387
return result;
2388
},
2389
2390
ModuleSpecifier: function (expr, precedence, flags) {
2391
return this.Literal(expr, precedence, flags);
2392
}
2393
2394
};
2395
2396
merge(CodeGenerator.prototype, CodeGenerator.Expression);
2397
2398
CodeGenerator.prototype.generateExpression = function (expr, precedence, flags) {
2399
var result, type;
2400
2401
type = expr.type || Syntax.Property;
2402
2403
if (extra.verbatim && expr.hasOwnProperty(extra.verbatim)) {
2404
return generateVerbatim(expr, precedence);
2405
}
2406
2407
result = this[type](expr, precedence, flags);
2408
2409
2410
if (extra.comment) {
2411
result = addComments(expr,result);
2412
}
2413
return toSourceNodeWhenNeeded(result, expr);
2414
};
2415
2416
CodeGenerator.prototype.generateStatement = function (stmt, flags) {
2417
var result,
2418
fragment;
2419
2420
result = this[stmt.type](stmt, flags);
2421
2422
// Attach comments
2423
2424
if (extra.comment) {
2425
result = addComments(stmt, result);
2426
}
2427
2428
fragment = toSourceNodeWhenNeeded(result).toString();
2429
if (stmt.type === Syntax.Program && !safeConcatenation && newline === '' && fragment.charAt(fragment.length - 1) === '\n') {
2430
result = sourceMap ? toSourceNodeWhenNeeded(result).replaceRight(/\s+$/, '') : fragment.replace(/\s+$/, '');
2431
}
2432
2433
return toSourceNodeWhenNeeded(result, stmt);
2434
};
2435
2436
function generateInternal(node) {
2437
var codegen;
2438
2439
codegen = new CodeGenerator();
2440
if (isStatement(node)) {
2441
return codegen.generateStatement(node, S_TFFF);
2442
}
2443
2444
if (isExpression(node)) {
2445
return codegen.generateExpression(node, Precedence.Sequence, E_TTT);
2446
}
2447
2448
throw new Error('Unknown node type: ' + node.type);
2449
}
2450
2451
function generate(node, options) {
2452
var defaultOptions = getDefaultOptions(), result, pair;
2453
2454
if (options != null) {
2455
// Obsolete options
2456
//
2457
// `options.indent`
2458
// `options.base`
2459
//
2460
// Instead of them, we can use `option.format.indent`.
2461
if (typeof options.indent === 'string') {
2462
defaultOptions.format.indent.style = options.indent;
2463
}
2464
if (typeof options.base === 'number') {
2465
defaultOptions.format.indent.base = options.base;
2466
}
2467
options = updateDeeply(defaultOptions, options);
2468
indent = options.format.indent.style;
2469
if (typeof options.base === 'string') {
2470
base = options.base;
2471
} else {
2472
base = stringRepeat(indent, options.format.indent.base);
2473
}
2474
} else {
2475
options = defaultOptions;
2476
indent = options.format.indent.style;
2477
base = stringRepeat(indent, options.format.indent.base);
2478
}
2479
json = options.format.json;
2480
renumber = options.format.renumber;
2481
hexadecimal = json ? false : options.format.hexadecimal;
2482
quotes = json ? 'double' : options.format.quotes;
2483
escapeless = options.format.escapeless;
2484
newline = options.format.newline;
2485
space = options.format.space;
2486
if (options.format.compact) {
2487
newline = space = indent = base = '';
2488
}
2489
parentheses = options.format.parentheses;
2490
semicolons = options.format.semicolons;
2491
safeConcatenation = options.format.safeConcatenation;
2492
directive = options.directive;
2493
parse = json ? null : options.parse;
2494
sourceMap = options.sourceMap;
2495
sourceCode = options.sourceCode;
2496
preserveBlankLines = options.format.preserveBlankLines && sourceCode !== null;
2497
extra = options;
2498
2499
if (sourceMap) {
2500
if (!exports.browser) {
2501
// We assume environment is node.js
2502
// And prevent from including source-map by browserify
2503
SourceNode = require('source-map').SourceNode;
2504
} else {
2505
SourceNode = global.sourceMap.SourceNode;
2506
}
2507
}
2508
2509
result = generateInternal(node);
2510
2511
if (!sourceMap) {
2512
pair = {code: result.toString(), map: null};
2513
return options.sourceMapWithCode ? pair : pair.code;
2514
}
2515
2516
2517
pair = result.toStringWithSourceMap({
2518
file: options.file,
2519
sourceRoot: options.sourceMapRoot
2520
});
2521
2522
if (options.sourceContent) {
2523
pair.map.setSourceContent(options.sourceMap,
2524
options.sourceContent);
2525
}
2526
2527
if (options.sourceMapWithCode) {
2528
return pair;
2529
}
2530
2531
return pair.map.toString();
2532
}
2533
2534
FORMAT_MINIFY = {
2535
indent: {
2536
style: '',
2537
base: 0
2538
},
2539
renumber: true,
2540
hexadecimal: true,
2541
quotes: 'auto',
2542
escapeless: true,
2543
compact: true,
2544
parentheses: false,
2545
semicolons: false
2546
};
2547
2548
FORMAT_DEFAULTS = getDefaultOptions().format;
2549
2550
exports.version = require('./package.json').version;
2551
exports.generate = generate;
2552
exports.attachComments = estraverse.attachComments;
2553
exports.Precedence = updateDeeply({}, Precedence);
2554
exports.browser = false;
2555
exports.FORMAT_MINIFY = FORMAT_MINIFY;
2556
exports.FORMAT_DEFAULTS = FORMAT_DEFAULTS;
2557
}());
2558
/* vim: set sw=4 ts=4 et tw=80 : */
2559
2560