'use strict'; var SourceMapGenerator = require('source-map').SourceMapGenerator; function offsetMapping(mapping, offset) { return { line: offset.line + mapping.line, column: offset.column + mapping.column }; } function newlinesIn(src) { if (!src) return 0; var newlines = src.match(/\n/g); return newlines ? newlines.length : 0; } function Generator(opts) { opts = opts || {}; this.generator = new SourceMapGenerator({ file: opts.file || '', sourceRoot: opts.sourceRoot || '' }); this.sourcesContent = undefined; this.opts = opts; } /** * Adds the given mappings to the generator and offsets them if offset is given * * @name addMappings * @function * @param sourceFile {String} name of the source file * @param mappings {Array{{Object}} each object has the form { original: { line: _, column: _ }, generated: { line: _, column: _ } } * @param offset {Object} offset to apply to each mapping. Has the form { line: _, column: _ } * @return {Object} the generator to allow chaining */ Generator.prototype.addMappings = function (sourceFile, mappings, offset) { var generator = this.generator; offset = offset || {}; offset.line = offset.hasOwnProperty('line') ? offset.line : 0; offset.column = offset.hasOwnProperty('column') ? offset.column : 0; mappings.forEach(function (m) { // only set source if we have original position to handle edgecase (see inline-source-map tests) generator.addMapping({ source : m.original ? sourceFile : undefined , original : m.original , generated : offsetMapping(m.generated, offset) }); }); return this; }; /** * Generates mappings for the given source, assuming that no translation from original to generated is necessary. * * @name addGeneratedMappings * @function * @param sourceFile {String} name of the source file * @param source {String} source of the file * @param offset {Object} offset to apply to each mapping. Has the form { line: _, column: _ } * @return {Object} the generator to allow chaining */ Generator.prototype.addGeneratedMappings = function (sourceFile, source, offset) { var mappings = [] , linesToGenerate = newlinesIn(source) + 1; for (var line = 1; line <= linesToGenerate; line++) { var location = { line: line, column: 0 }; mappings.push({ original: location, generated: location }); } return this.addMappings(sourceFile, mappings, offset); }; /** * Adds source content for the given source file. * * @name addSourceContent * @function * @param sourceFile {String} The source file for which a mapping is included * @param sourcesContent {String} The content of the source file * @return {Object} The generator to allow chaining */ Generator.prototype.addSourceContent = function (sourceFile, sourcesContent) { this.sourcesContent = this.sourcesContent || {}; this.sourcesContent[sourceFile] = sourcesContent; return this; }; /** * @name base64Encode * @function * @return {String} bas64 encoded representation of the added mappings */ Generator.prototype.base64Encode = function () { var map = this.toString(); return new Buffer(map).toString('base64'); }; /** * @name inlineMappingUrl * @function * @return {String} comment with base64 encoded representation of the added mappings. Can be inlined at the end of the generated file. */ Generator.prototype.inlineMappingUrl = function () { var charset = this.opts.charset || 'utf-8'; return '//# sourceMappingURL=data:application/json;charset=' + charset + ';base64,' + this.base64Encode(); }; Generator.prototype.toJSON = function () { var map = this.generator.toJSON(); if (!this.sourcesContent) return map; var toSourcesContent = (function (s) { if (typeof this.sourcesContent[s] === 'string') { return this.sourcesContent[s]; } else { return null; } }).bind(this); map.sourcesContent = map.sources.map(toSourcesContent); return map; }; Generator.prototype.toString = function () { return JSON.stringify(this); }; Generator.prototype._mappings = function () { return this.generator._mappings._array; }; Generator.prototype.gen = function () { return this.generator; }; module.exports = function (opts) { return new Generator(opts); }; module.exports.Generator = Generator;