Files
less.js/lib/less/source-map-output.js
2016-03-04 17:55:55 -05:00

147 lines
5.2 KiB
JavaScript

module.exports = function (environment) {
/**
* @param source The code
* @param ignoredCharsCount Number of characters at the start of the file to ignore.
* @constructor
*/
var FileInfo = function (source, ignoredCharsCount) {
this.ignoredCharsCount = ignoredCharsCount;
this.source = source.slice(ignoredCharsCount);
this.sourceLines = this.source.split('\n');
};
/** Translate absolute source offset to line/column offset. */
FileInfo.prototype.getLocation = function (index) {
index = Math.max(0, index - this.ignoredCharsCount);
var line = 0;
for (; line < this.sourceLines.length && index >= this.sourceLines[line].length + 1; line++) {
index -= this.sourceLines[line].length + 1; // +1 for the '\n' character
}
return {line: line + 1, column: index};
};
var SourceMapOutput = function (options) {
this._css = [];
this._rootNode = options.rootNode;
this._contentsInfoMap = {};
for (var key in options.contentsMap) {
if (options.contentsMap.hasOwnProperty(key)) {
this._contentsInfoMap[key] = new FileInfo(
options.contentsMap[key], options.contentsIgnoredCharsMap[key] || 0);
}
}
if (options.sourceMapFilename) {
this._sourceMapFilename = options.sourceMapFilename.replace(/\\/g, '/');
}
this._outputFilename = options.outputFilename;
this.sourceMapURL = options.sourceMapURL;
if (options.sourceMapBasepath) {
this._sourceMapBasepath = options.sourceMapBasepath.replace(/\\/g, '/');
}
if (options.sourceMapRootpath) {
this._sourceMapRootpath = options.sourceMapRootpath.replace(/\\/g, '/');
if (this._sourceMapRootpath.charAt(this._sourceMapRootpath.length - 1) !== '/') {
this._sourceMapRootpath += '/';
}
} else {
this._sourceMapRootpath = "";
}
this._outputSourceFiles = options.outputSourceFiles;
this._sourceMapGeneratorConstructor = environment.getSourceMapGenerator();
this._lineNumber = 0;
this._column = 0;
};
SourceMapOutput.prototype.normalizeFilename = function(filename) {
filename = filename.replace(/\\/g, '/');
if (this._sourceMapBasepath && filename.indexOf(this._sourceMapBasepath) === 0) {
filename = filename.substring(this._sourceMapBasepath.length);
if (filename.charAt(0) === '\\' || filename.charAt(0) === '/') {
filename = filename.substring(1);
}
}
return (this._sourceMapRootpath || "") + filename;
};
SourceMapOutput.prototype.add = function(chunk, fileInfo, index, mapLines) {
//ignore adding empty strings
if (!chunk) {
return;
}
var lines,
columns,
i;
lines = chunk.split("\n");
columns = lines[lines.length - 1];
if (fileInfo) {
var location = this._contentsInfoMap[fileInfo.filename].getLocation(index);
if (!mapLines) {
this._sourceMapGenerator.addMapping({ generated: { line: this._lineNumber + 1, column: this._column},
original: location,
source: this.normalizeFilename(fileInfo.filename)});
} else {
for (i = 0; i < lines.length; i++) {
this._sourceMapGenerator.addMapping({ generated: { line: this._lineNumber + i + 1, column: i === 0 ? this._column : 0},
original: { line: location.line + i, column: i === 0 ? location.column : 0},
source: this.normalizeFilename(fileInfo.filename)});
}
}
}
if (lines.length === 1) {
this._column += columns.length;
} else {
this._lineNumber += lines.length - 1;
this._column = columns.length;
}
this._css.push(chunk);
};
SourceMapOutput.prototype.isEmpty = function() {
return this._css.length === 0;
};
SourceMapOutput.prototype.toCSS = function(context) {
this._sourceMapGenerator = new this._sourceMapGeneratorConstructor({ file: this._outputFilename, sourceRoot: null });
if (this._outputSourceFiles) {
for (var filename in this._contentsMap) {
if (this._contentsInfoMap.hasOwnProperty(filename)) {
var source = this._contentsInfoMap[filename].source;
this._sourceMapGenerator.setSourceContent(this.normalizeFilename(filename), source);
}
}
}
this._rootNode.genCSS(context, this);
if (this._css.length > 0) {
var sourceMapURL,
sourceMapContent = JSON.stringify(this._sourceMapGenerator.toJSON());
if (this.sourceMapURL) {
sourceMapURL = this.sourceMapURL;
} else if (this._sourceMapFilename) {
sourceMapURL = this._sourceMapFilename;
}
this.sourceMapURL = sourceMapURL;
this.sourceMap = sourceMapContent;
}
return this._css.join('');
};
return SourceMapOutput;
};