nunta/node_modules/grunt-contrib-concat/tasks/lib/sourcemap.js
David Miller 0025e7e961 setting up Gruntfile and dist folder
index.html, mail/, bootstrap and jquery bower components will build to
the dist folder when running grunt
2014-08-12 21:01:38 -04:00

209 lines
6.8 KiB
JavaScript

/*
* grunt-contrib-concat
* http://gruntjs.com/
*
* Copyright (c) 2013 "Cowboy" Ben Alman, contributors
* Licensed under the MIT license.
*/
'use strict';
exports.init = function(grunt) {
var exports = {};
// Node first party libs
var path = require('path');
// Third party libs
var chalk = require('chalk');
var SourceMapConsumer = require('source-map').SourceMapConsumer;
var SourceMapGenerator = require('source-map').SourceMapGenerator;
var SourceNode = require('source-map').SourceNode;
// Return an object that is used to track sourcemap data between calls.
exports.helper = function(files, options) {
// Figure out the source map destination.
var dest = files.dest;
if (options.sourceMapStyle === 'inline') {
// Leave dest as is. It will be used to compute relative sources.
} else if (typeof options.sourceMapName === 'string') {
dest = options.sourceMapName;
} else if (typeof options.sourceMapName === 'function') {
dest = options.sourceMapName(dest);
} else {
dest = dest + '.map';
}
// Inline style and sourceMapName together doesn't work
if (options.sourceMapStyle === 'inline' && options.sourceMapName) {
grunt.log.warn(
'Source map will be inlined, sourceMapName option ignored.'
);
}
return new SourceMapConcatHelper({
files: files,
dest: dest,
options: options
});
};
function SourceMapConcatHelper(options) {
this.files = options.files;
this.dest = options.dest;
this.options = options.options;
// Create the source map node we'll add concat files into.
this.node = new SourceNode();
// Create an array to store source maps that are referenced from files
// being concatenated.
this.maps = [];
}
// Construct a node split by a zero-length regex.
SourceMapConcatHelper.prototype._dummyNode = function(src, name) {
var node = new SourceNode();
var lineIndex = 1;
var charIndex = 0;
var tokens = src.split(/\b/g);
tokens.forEach(function(token, i) {
node.add(new SourceNode(lineIndex, charIndex, name, token));
// Count lines and chars.
token.split('\n').forEach(function(subtoken, j, subtokens) {
if (j < subtokens.length - 1) {
lineIndex++;
charIndex = 0;
} else {
charIndex += subtoken.length;
}
});
});
return node;
};
// Add some arbitraty text to the sourcemap.
SourceMapConcatHelper.prototype.add = function(src) {
// Use the dummy node to track new lines and character offset in the unnamed
// concat pieces (banner, footer, separator).
this.node.add(this._dummyNode(src));
};
// Add the lines of a given file to the sourcemap. If in the file, store a
// prior sourcemap and return src with sourceMappingURL removed.
SourceMapConcatHelper.prototype.addlines = function(src, filename) {
var relativeFilename = path.relative(path.dirname(this.dest), filename);
var node;
if (
/\/\/[@#]\s+sourceMappingURL=(.+)/.test(src) ||
/\/\*#\s+sourceMappingURL=(\S+)\s+\*\//.test(src)
) {
var sourceMapFile = RegExp.$1;
var sourceMapPath;
var sourceContent;
// Browserify, as an example, stores a datauri at sourceMappingURL.
if (/data:application\/json;base64,([^\s]+)/.test(sourceMapFile)) {
// Set sourceMapPath to the file that the map is inlined.
sourceMapPath = filename;
sourceContent = new Buffer(RegExp.$1, 'base64').toString();
} else {
// If sourceMapPath is relative, expand relative to the file
// refering to it.
sourceMapPath = path.resolve(path.dirname(filename), sourceMapFile);
sourceContent = grunt.file.read(sourceMapPath);
}
var sourceMap = JSON.parse(sourceContent);
var sourceMapConsumer = new SourceMapConsumer(sourceMap);
// Consider the relative path from source files to new sourcemap.
var sourcePathToSourceMapPath =
path.relative(path.dirname(this.dest), path.dirname(sourceMapPath));
// Store the sourceMap so that it may later be consumed.
this.maps.push([
sourceMapConsumer, relativeFilename, sourcePathToSourceMapPath
]);
// Remove the old sourceMappingURL.
src = src.replace(/[@#]\s+sourceMappingURL=[^\s]+/, '');
// Create a node from the source map for the file.
node = SourceNode.fromStringWithSourceMap(
src, sourceMapConsumer, sourcePathToSourceMapPath
);
} else {
// Use a dummy node. Performs a rudimentary tokenization of the source.
node = this._dummyNode(src, relativeFilename);
}
this.node.add(node);
if (this.options.sourceMapStyle !== 'link') {
this.node.setSourceContent(relativeFilename, src);
}
return src;
};
// Return the comment sourceMappingURL that must be appended to the
// concatenated file.
SourceMapConcatHelper.prototype.url = function() {
// Create the map filepath. Either datauri or destination path.
var mapfilepath;
if (this.options.sourceMapStyle === 'inline') {
var inlineMap = new Buffer(this._write()).toString('base64');
mapfilepath = 'data:application/json;base64,' + inlineMap;
} else {
// Compute relative path to source map destination.
mapfilepath = path.relative(path.dirname(this.files.dest), this.dest);
}
// Create the sourceMappingURL.
var url;
if (/\.css$/.test(this.files.dest)) {
url = '\n/*# sourceMappingURL=' + mapfilepath + ' */';
} else {
url = '\n//# sourceMappingURL=' + mapfilepath;
}
return url;
};
// Return a string for inline use or write the source map to disk.
SourceMapConcatHelper.prototype._write = function() {
var code_map = this.node.toStringWithSourceMap({
file: path.relative(path.dirname(this.dest), this.files.dest)
});
// Consume the new sourcemap.
var generator = SourceMapGenerator.fromSourceMap(
new SourceMapConsumer(code_map.map.toJSON())
);
// Consume sourcemaps for source files.
this.maps.forEach(Function.apply.bind(generator.applySourceMap, generator));
// New sourcemap.
var newSourceMap = generator.toJSON();
// Return a string for inline use or write the map.
if (this.options.sourceMapStyle === 'inline') {
grunt.log.writeln(
'Source map for ' + chalk.cyan(this.files.dest) + ' inlined.'
);
return JSON.stringify(newSourceMap, null, '');
} else {
grunt.file.write(
this.dest,
JSON.stringify(newSourceMap, null, '')
);
grunt.log.writeln('Source map ' + chalk.cyan(this.dest) + ' created.');
}
};
// Non-private function to write the sourcemap. Shortcuts if writing a inline
// style map.
SourceMapConcatHelper.prototype.write = function() {
if (this.options.sourceMapStyle !== 'inline') {
this._write();
}
};
return exports;
};