source: frontend/node_modules/escodegen/escodegen.js

Last change on this file was 9af201e, checked in by MBK <marija.karapandzova@…>, 11 days ago

Fix frontend appearance

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