grunt-contrib-concat = function (grunt) { // Internal lib. var comment = require('./lib/comment').init(grunt); var chalk = require('chalk'); var sourcemap = require('./lib/sourcemap').init(grunt); grunt.registerMultiTask('concat', 'Concatenate files.', function() { // Merge task-specific and/or target-specific options with these defaults. var options = this.options({ separator: grunt.util.linefeed, banner: '', footer: '', stripBanners: false, process: false, sourceMap: false, sourceMapName: undefined, sourceMapStyle: 'embed' }); // Normalize boolean options that accept options objects. if (options.stripBanners === true) { options.stripBanners = {}; } if (options.process === true) { options.process = {}; } // Process banner and footer. var banner = grunt.template.process(options.banner); var footer = grunt.template.process(options.footer); // Set a local variable for whether to build source maps or not. var sourceMap = options.sourceMap; // If content is not embedded and it will be modified, either exit or do // not make the source map. if ( sourceMap && options.sourceMapStyle === 'link' && (options.stripBanners || options.process) ) { // Warn and exit if --force isn't set. grunt.warn( 'stripBanners or process option is enabled. ' + 'Set sourceMapStyle option to \'embed\' or \'inline\'.' ); // --force is set, continue on without the source map. grunt.log.warn('Skipping creation of source maps.'); // Set sourceMap to false to keep maps from being constructed. sourceMap = false; } // Iterate over all src-dest file pairs. this.files.forEach(function(f) { // Initialize source map objects. var sourceMapHelper; if (sourceMap) { sourceMapHelper = sourcemap.helper(f, options); sourceMapHelper.add(banner); } // Concat banner + specified files + footer. var src = banner + f.src.filter(function(filepath) { // Warn on and remove invalid source files (if nonull was set). if (!grunt.file.exists(filepath)) { grunt.log.warn('Source file "' + filepath + '" not found.'); return false; } return true; }).map(function(filepath, i) { if (grunt.file.isDir(filepath)) { return; } // Read file source. var src = grunt.file.read(filepath); // Process files as templates if requested. if (typeof options.process === 'function') { src = options.process(src, filepath); } else if (options.process) { src = grunt.template.process(src, options.process); } // Strip banners if requested. if (options.stripBanners) { src = comment.stripBanner(src, options.stripBanners); } // Add the lines of this file to our map. if (sourceMapHelper) { src = sourceMapHelper.addlines(src, filepath); if (i < f.src.length - 1) { sourceMapHelper.add(options.separator); } } return src; }).join(options.separator) + footer; if (sourceMapHelper) { sourceMapHelper.add(footer); sourceMapHelper.write(); // Add sourceMappingURL to the end. src += sourceMapHelper.url(); } // Write the destination file. grunt.file.write(f.dest, src); // Print a success message. grunt.verbose.write('File ' + chalk.cyan(f.dest) + ' created.'); }); }); }
n/a
init = function () { var exports = {}; // Return the given source code with any leading banner comment stripped. exports.stripBanner = function(src, options) { if (!options) { options = {}; } var m = []; if (options.line) { // Strip // ... leading banners. m.push('(?:.*\\/\\/.*\\r?\\n)+\\s*'); } if (options.block) { // Strips all /* ... */ block comment banners. m.push('\\/\\*[\\s\\S]*?\\*\\/'); } else { // Strips only /* ... */ block comment banners, excluding /*! ... */. m.push('\\/\\*[^!][\\s\\S]*?\\*\\/'); } var re = new RegExp('^\\s*(?:' + m.join('|') + ')\\s*', ''); return src.replace(re, ''); }; return exports; }
n/a
init = function (grunt) {
var exports = {};
// Node first party libs
var path = require('path');
// Third party libs
var chalk = require('chalk');
var SourceMap = require('source-map');
var SourceMapConsumer = SourceMap.SourceMapConsumer;
var SourceMapGenerator = SourceMap.SourceMapGenerator;
var NO_OP = function(){};
function SourceMapConcatHelper(options) {
this.files = options.files;
this.dest = options.dest;
this.options = options.options;
this.line = 1;
this.column = 0;
// ensure we're using forward slashes, because these are URLs
var file = path.relative(path.dirname(this.dest), this.files.dest).replace(/\\/g, '/');
var generator = new SourceMapGenerator({
file: file
});
this.file = file;
this.generator = generator;
this.addMapping = function(genLine, genCol, orgLine, orgCol, source, name) {
if (!source) {
generator.addMapping({
generated: {line: genLine, column: genCol}
});
} else {
if (!name) {
generator.addMapping({
generated: {line: genLine, column: genCol},
original: {line: orgLine, column: orgCol},
source: source
});
} else {
generator.addMapping({
generated: {line: genLine, column: genCol},
original: {line: orgLine, column: orgCol},
source: source,
name: name
});
}
}
};
}
// 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 += '.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
});
};
// Parse only to increment the generated file's column and line count
SourceMapConcatHelper.prototype.add = function(src) {
this._forEachTokenPosition(src);
};
/**
* Parse the source file into tokens and apply the provided callback
* with the position of the token boundaries in the original file, and
* in the generated file.
*
* @param src The sources to tokenize. Required
* @param filename The name of the source file. Optional
* @param callback What to do with the token position indices. Optional
*/
SourceMapConcatHelper.prototype._forEachTokenPosition = function(src, filename, callback) {
var genLine = this.line;
var genCol = this.column;
var orgLine = 1;
var orgCol = 0;
// Tokenize on words, new lines, and white space.
var tokens = src.split(/(\n|[^\S\n]+|\b)/g);
if (!callback) {
callback = NO_OP;
}
for (var i = 0, len = tokens.length; i < len; i++) {
var token = tokens[i];
if (token) {
// The if statement filters out empty strings.
callback(genLine, genCol, orgLine, orgCol, filename);
if (token === '\n') {
++orgLine;
++genLine;
orgCol = 0;
genCol = 0;
} else {
orgCol += token.length;
genCol += token.length;
}
}
}
this.line = genLine;
this.column = genCol;
};
// 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 sourceMapRegEx = /\n\/[*/][@#]\s+sourceMappingURL=((?:(?!\s+\*\/).)*).*/;
var relativeFilena ...
n/a