UNPKG

14.6 kBJavaScriptView Raw
1"use strict";
2
3// istanbul ignore next
4
5var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
6
7// istanbul ignore next
8
9function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj["default"] = obj; return newObj; } }
10
11// istanbul ignore next
12
13function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
14
15// istanbul ignore next
16
17function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
18
19var _detectIndent = require("detect-indent");
20
21var _detectIndent2 = _interopRequireDefault(_detectIndent);
22
23var _whitespace = require("./whitespace");
24
25var _whitespace2 = _interopRequireDefault(_whitespace);
26
27var _nodePrinter = require("./node/printer");
28
29var _nodePrinter2 = _interopRequireDefault(_nodePrinter);
30
31var _repeating = require("repeating");
32
33var _repeating2 = _interopRequireDefault(_repeating);
34
35var _sourceMap = require("./source-map");
36
37var _sourceMap2 = _interopRequireDefault(_sourceMap);
38
39var _position = require("./position");
40
41var _position2 = _interopRequireDefault(_position);
42
43var _messages = require("../messages");
44
45var messages = _interopRequireWildcard(_messages);
46
47var _buffer = require("./buffer");
48
49var _buffer2 = _interopRequireDefault(_buffer);
50
51var _lodashObjectExtend = require("lodash/object/extend");
52
53var _lodashObjectExtend2 = _interopRequireDefault(_lodashObjectExtend);
54
55var _lodashCollectionEach = require("lodash/collection/each");
56
57var _lodashCollectionEach2 = _interopRequireDefault(_lodashCollectionEach);
58
59var _node2 = require("./node");
60
61var _node3 = _interopRequireDefault(_node2);
62
63var _types = require("../types");
64
65var t = _interopRequireWildcard(_types);
66
67/**
68 * Babel's code generator, turns an ast into code, maintaining sourcemaps,
69 * user preferences, and valid output.
70 */
71
72var CodeGenerator = (function () {
73 function CodeGenerator(ast, opts, code) {
74 _classCallCheck(this, CodeGenerator);
75
76 opts = opts || {};
77
78 this.comments = ast.comments || [];
79 this.tokens = ast.tokens || [];
80 this.format = CodeGenerator.normalizeOptions(code, opts, this.tokens);
81 this.opts = opts;
82 this.ast = ast;
83
84 this.whitespace = new _whitespace2["default"](this.tokens);
85 this.position = new _position2["default"]();
86 this.map = new _sourceMap2["default"](this.position, opts, code);
87 this.buffer = new _buffer2["default"](this.position, this.format);
88 }
89
90 /**
91 * [Please add a description.]
92 */
93
94 /**
95 * Normalize generator options, setting defaults.
96 *
97 * - Detects code indentation.
98 * - If `opts.compact = "auto"` and the code is over 100KB, `compact` will be set to `true`.
99 */
100
101 CodeGenerator.normalizeOptions = function normalizeOptions(code, opts, tokens) {
102 var style = " ";
103 if (code) {
104 var indent = _detectIndent2["default"](code).indent;
105 if (indent && indent !== " ") style = indent;
106 }
107
108 var format = {
109 shouldPrintComment: opts.shouldPrintComment,
110 retainLines: opts.retainLines,
111 comments: opts.comments == null || opts.comments,
112 compact: opts.compact,
113 quotes: CodeGenerator.findCommonStringDelimiter(code, tokens),
114 indent: {
115 adjustMultilineComment: true,
116 style: style,
117 base: 0
118 }
119 };
120
121 if (format.compact === "auto") {
122 format.compact = code.length > 100000; // 100KB
123
124 if (format.compact) {
125 console.error("[BABEL] " + messages.get("codeGeneratorDeopt", opts.filename, "100KB"));
126 }
127 }
128
129 if (format.compact) {
130 format.indent.adjustMultilineComment = false;
131 }
132
133 return format;
134 };
135
136 /**
137 * Determine if input code uses more single or double quotes.
138 */
139
140 CodeGenerator.findCommonStringDelimiter = function findCommonStringDelimiter(code, tokens) {
141 var occurences = {
142 single: 0,
143 double: 0
144 };
145
146 var checked = 0;
147
148 for (var i = 0; i < tokens.length; i++) {
149 var token = tokens[i];
150 if (token.type.label !== "string") continue;
151
152 var raw = code.slice(token.start, token.end);
153 if (raw[0] === "'") {
154 occurences.single++;
155 } else {
156 occurences.double++;
157 }
158
159 checked++;
160 if (checked >= 3) break;
161 }
162 if (occurences.single > occurences.double) {
163 return "single";
164 } else {
165 return "double";
166 }
167 };
168
169 /**
170 * All node generators.
171 */
172
173 /**
174 * Generate code and sourcemap from ast.
175 *
176 * Appends comments that weren't attached to any node to the end of the generated output.
177 */
178
179 CodeGenerator.prototype.generate = function generate() {
180 var ast = this.ast;
181
182 this.print(ast);
183
184 if (ast.comments) {
185 var comments = [];
186 var _arr = ast.comments;
187 for (var _i = 0; _i < _arr.length; _i++) {
188 var comment = _arr[_i];
189 if (!comment._displayed) comments.push(comment);
190 }
191 this._printComments(comments);
192 }
193
194 return {
195 map: this.map.get(),
196 code: this.buffer.get()
197 };
198 };
199
200 /**
201 * Build NodePrinter.
202 */
203
204 CodeGenerator.prototype.buildPrint = function buildPrint(parent) {
205 return new _nodePrinter2["default"](this, parent);
206 };
207
208 /**
209 * [Please add a description.]
210 */
211
212 CodeGenerator.prototype.catchUp = function catchUp(node) {
213 // catch up to this nodes newline if we're behind
214 if (node.loc && this.format.retainLines && this.buffer.buf) {
215 while (this.position.line < node.loc.start.line) {
216 this._push("\n");
217 }
218 }
219 };
220
221 /**
222 * [Please add a description.]
223 */
224
225 CodeGenerator.prototype._printNewline = function _printNewline(leading, node, parent, opts) {
226 if (!opts.statement && !_node3["default"].isUserWhitespacable(node, parent)) {
227 return;
228 }
229
230 var lines = 0;
231
232 if (node.start != null && !node._ignoreUserWhitespace) {
233 // user node
234 if (leading) {
235 lines = this.whitespace.getNewlinesBefore(node);
236 } else {
237 lines = this.whitespace.getNewlinesAfter(node);
238 }
239 } else {
240 // generated node
241 if (!leading) lines++; // always include at least a single line after
242 if (opts.addNewlines) lines += opts.addNewlines(leading, node) || 0;
243
244 var needs = _node3["default"].needsWhitespaceAfter;
245 if (leading) needs = _node3["default"].needsWhitespaceBefore;
246 if (needs(node, parent)) lines++;
247
248 // generated nodes can't add starting file whitespace
249 if (!this.buffer.buf) lines = 0;
250 }
251
252 this.newline(lines);
253 };
254
255 /**
256 * [Please add a description.]
257 */
258
259 CodeGenerator.prototype.print = function print(node, parent) {
260 var opts = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2];
261
262 if (!node) return;
263
264 if (parent && parent._compact) {
265 node._compact = true;
266 }
267
268 var oldConcise = this.format.concise;
269 if (node._compact) {
270 this.format.concise = true;
271 }
272
273 if (!this[node.type]) {
274 throw new ReferenceError("unknown node of type " + JSON.stringify(node.type) + " with constructor " + JSON.stringify(node && node.constructor.name));
275 }
276
277 var needsParens = _node3["default"].needsParens(node, parent);
278 if (needsParens) this.push("(");
279
280 this.printLeadingComments(node, parent);
281
282 this.catchUp(node);
283
284 this._printNewline(true, node, parent, opts);
285
286 if (opts.before) opts.before();
287 this.map.mark(node, "start");
288
289 this[node.type](node, this.buildPrint(node), parent);
290
291 if (needsParens) this.push(")");
292
293 this.map.mark(node, "end");
294 if (opts.after) opts.after();
295
296 this.format.concise = oldConcise;
297
298 this._printNewline(false, node, parent, opts);
299
300 this.printTrailingComments(node, parent);
301 };
302
303 /**
304 * [Please add a description.]
305 */
306
307 CodeGenerator.prototype.printJoin = function printJoin(print, nodes) {
308 // istanbul ignore next
309
310 var _this = this;
311
312 var opts = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2];
313
314 if (!nodes || !nodes.length) return;
315
316 var len = nodes.length;
317
318 if (opts.indent) this.indent();
319
320 var printOpts = {
321 statement: opts.statement,
322 addNewlines: opts.addNewlines,
323 after: function after() {
324 if (opts.iterator) {
325 opts.iterator(node, i);
326 }
327
328 if (opts.separator && i < len - 1) {
329 _this.push(opts.separator);
330 }
331 }
332 };
333
334 for (var i = 0; i < nodes.length; i++) {
335 var node = nodes[i];
336 print.plain(node, printOpts);
337 }
338
339 if (opts.indent) this.dedent();
340 };
341
342 /**
343 * [Please add a description.]
344 */
345
346 CodeGenerator.prototype.printAndIndentOnComments = function printAndIndentOnComments(print, node) {
347 var indent = !!node.leadingComments;
348 if (indent) this.indent();
349 print.plain(node);
350 if (indent) this.dedent();
351 };
352
353 /**
354 * [Please add a description.]
355 */
356
357 CodeGenerator.prototype.printBlock = function printBlock(print, node) {
358 if (t.isEmptyStatement(node)) {
359 this.semicolon();
360 } else {
361 this.push(" ");
362 print.plain(node);
363 }
364 };
365
366 /**
367 * [Please add a description.]
368 */
369
370 CodeGenerator.prototype.generateComment = function generateComment(comment) {
371 var val = comment.value;
372 if (comment.type === "CommentLine") {
373 val = "//" + val;
374 } else {
375 val = "/*" + val + "*/";
376 }
377 return val;
378 };
379
380 /**
381 * [Please add a description.]
382 */
383
384 CodeGenerator.prototype.printTrailingComments = function printTrailingComments(node, parent) {
385 this._printComments(this.getComments("trailingComments", node, parent));
386 };
387
388 /**
389 * [Please add a description.]
390 */
391
392 CodeGenerator.prototype.printLeadingComments = function printLeadingComments(node, parent) {
393 this._printComments(this.getComments("leadingComments", node, parent));
394 };
395
396 /**
397 * [Please add a description.]
398 */
399
400 CodeGenerator.prototype.getComments = function getComments(key, node, parent) {
401 if (t.isExpressionStatement(parent)) {
402 return [];
403 }
404
405 var comments = [];
406 var nodes = [node];
407
408 if (t.isExpressionStatement(node)) {
409 nodes.push(node.argument);
410 }
411
412 var _arr2 = nodes;
413 for (var _i2 = 0; _i2 < _arr2.length; _i2++) {
414 var _node = _arr2[_i2];
415 comments = comments.concat(this._getComments(key, _node));
416 }
417
418 return comments;
419 };
420
421 /**
422 * [Please add a description.]
423 */
424
425 CodeGenerator.prototype._getComments = function _getComments(key, node) {
426 return node && node[key] || [];
427 };
428
429 /**
430 * [Please add a description.]
431 */
432
433 CodeGenerator.prototype.shouldPrintComment = function shouldPrintComment(comment) {
434 if (this.format.shouldPrintComment) {
435 return this.format.shouldPrintComment(comment.value);
436 } else {
437 if (comment.value.indexOf("@license") >= 0 || comment.value.indexOf("@preserve") >= 0) {
438 return true;
439 } else {
440 return this.format.comments;
441 }
442 }
443 };
444
445 /**
446 * [Please add a description.]
447 */
448
449 CodeGenerator.prototype._printComments = function _printComments(comments) {
450 if (!comments || !comments.length) return;
451
452 var _arr3 = comments;
453 for (var _i3 = 0; _i3 < _arr3.length; _i3++) {
454 var comment = _arr3[_i3];
455 if (!this.shouldPrintComment(comment)) continue;
456 if (comment._displayed) continue;
457 comment._displayed = true;
458
459 this.catchUp(comment);
460
461 // whitespace before
462 this.newline(this.whitespace.getNewlinesBefore(comment));
463
464 var column = this.position.column;
465 var val = this.generateComment(comment);
466
467 if (column && !this.isLast(["\n", " ", "[", "{"])) {
468 this._push(" ");
469 column++;
470 }
471
472 //
473 if (comment.type === "CommentBlock" && this.format.indent.adjustMultilineComment) {
474 var offset = comment.loc && comment.loc.start.column;
475 if (offset) {
476 var newlineRegex = new RegExp("\\n\\s{1," + offset + "}", "g");
477 val = val.replace(newlineRegex, "\n");
478 }
479
480 var indent = Math.max(this.indentSize(), column);
481 val = val.replace(/\n/g, "\n" + _repeating2["default"](" ", indent));
482 }
483
484 if (column === 0) {
485 val = this.getIndent() + val;
486 }
487
488 // force a newline for line comments when retainLines is set in case the next printed node
489 // doesn't catch up
490 if ((this.format.compact || this.format.retainLines) && comment.type === "CommentLine") {
491 val += "\n";
492 }
493
494 //
495 this._push(val);
496
497 // whitespace after
498 this.newline(this.whitespace.getNewlinesAfter(comment));
499 }
500 };
501
502 _createClass(CodeGenerator, null, [{
503 key: "generators",
504 value: {
505 templateLiterals: require("./generators/template-literals"),
506 comprehensions: require("./generators/comprehensions"),
507 expressions: require("./generators/expressions"),
508 statements: require("./generators/statements"),
509 classes: require("./generators/classes"),
510 methods: require("./generators/methods"),
511 modules: require("./generators/modules"),
512 types: require("./generators/types"),
513 flow: require("./generators/flow"),
514 base: require("./generators/base"),
515 jsx: require("./generators/jsx")
516 },
517 enumerable: true
518 }]);
519
520 return CodeGenerator;
521})();
522
523_lodashCollectionEach2["default"](_buffer2["default"].prototype, function (fn, key) {
524 CodeGenerator.prototype[key] = function () {
525 return fn.apply(this.buffer, arguments);
526 };
527});
528
529/**
530 * [Please add a description.]
531 */
532
533_lodashCollectionEach2["default"](CodeGenerator.generators, function (generator) {
534 _lodashObjectExtend2["default"](CodeGenerator.prototype, generator);
535});
536
537/**
538 * [Please add a description.]
539 */
540
541module.exports = function (ast, opts, code) {
542 var gen = new CodeGenerator(ast, opts, code);
543 return gen.generate();
544};
545
546module.exports.CodeGenerator = CodeGenerator;
\No newline at end of file