/* global define */ import {isArray} from '../utils'; let SourceNode; try { /* istanbul ignore next */ if (typeof define !== 'function' || !define.amd) { // We don't support this in AMD environments. For these environments, we asusme that // they are running on the browser and thus have no need for the source-map library. let SourceMap = require('source-map'); SourceNode = SourceMap.SourceNode; } } catch (err) { /* NOP */ } /* istanbul ignore if: tested but not covered in istanbul due to dist build */ if (!SourceNode) { SourceNode = function(line, column, srcFile, chunks) { this.src = ''; if (chunks) { this.add(chunks); } }; /* istanbul ignore next */ SourceNode.prototype = { add: function(chunks) { if (isArray(chunks)) { chunks = chunks.join(''); } this.src += chunks; }, prepend: function(chunks) { if (isArray(chunks)) { chunks = chunks.join(''); } this.src = chunks + this.src; }, toStringWithSourceMap: function() { return {code: this.toString()}; }, toString: function() { return this.src; } }; } function castChunk(chunk, codeGen, loc) { if (isArray(chunk)) { let ret = []; for (let i = 0, len = chunk.length; i < len; i++) { ret.push(codeGen.wrap(chunk[i], loc)); } return ret; } else if (typeof chunk === 'boolean' || typeof chunk === 'number') { // Handle primitives that the SourceNode will throw up on return chunk + ''; } return chunk; } function CodeGen(srcFile) { this.srcFile = srcFile; this.source = []; } CodeGen.prototype = { isEmpty() { return !this.source.length; }, prepend: function(source, loc) { this.source.unshift(this.wrap(source, loc)); }, push: function(source, loc) { this.source.push(this.wrap(source, loc)); }, merge: function() { let source = this.empty(); this.each(function(line) { source.add([' ', line, '\n']); }); return source; }, each: function(iter) { for (let i = 0, len = this.source.length; i < len; i++) { iter(this.source[i]); } }, empty: function() { let loc = this.currentLocation || {start: {}}; return new SourceNode(loc.start.line, loc.start.column, this.srcFile); }, wrap: function(chunk, loc = this.currentLocation || {start: {}}) { if (chunk instanceof SourceNode) { return chunk; } chunk = castChunk(chunk, this, loc); return new SourceNode(loc.start.line, loc.start.column, this.srcFile, chunk); }, functionCall: function(fn, type, params) { params = this.generateList(params); return this.wrap([fn, type ? '.' + type + '(' : '(', params, ')']); }, quotedString: function(str) { return '"' + (str + '') .replace(/\\/g, '\\\\') .replace(/"/g, '\\"') .replace(/\n/g, '\\n') .replace(/\r/g, '\\r') .replace(/\u2028/g, '\\u2028') // Per Ecma-262 7.3 + 7.8.4 .replace(/\u2029/g, '\\u2029') + '"'; }, objectLiteral: function(obj) { let pairs = []; for (let key in obj) { if (obj.hasOwnProperty(key)) { let value = castChunk(obj[key], this); if (value !== 'undefined') { pairs.push([this.quotedString(key), ':', value]); } } } let ret = this.generateList(pairs); ret.prepend('{'); ret.add('}'); return ret; }, generateList: function(entries) { let ret = this.empty(); for (let i = 0, len = entries.length; i < len; i++) { if (i) { ret.add(','); } ret.add(castChunk(entries[i], this)); } return ret; }, generateArray: function(entries) { let ret = this.generateList(entries); ret.prepend('['); ret.add(']'); return ret; } }; export default CodeGen;