<commit_before>/**
 * Concatenate installed Bower packages
 *
 * @author <NAME> (http://sapegin.me)
 */


/*jshint node:true */
module.exports = function(grunt) {
	'use strict';

	var path = require('path');
	var fs = require('fs');
	var filesize = require('filesize');
	var bower = require('bower');
	var detective = require('detective');
	var async = require('async');
	var _ = require('lodash');
	_.str = require('underscore.string');
	var dependencyTools = require('../lib/dependencyTools');

	grunt.registerMultiTask('bower_concat', 'Concatenate installed Bower packages.', function() {
		var jsDest = this.data.dest;
		var cssDest = this.data.cssDest;

		// Require at least one of [`dest`, `cssDest`]
		if (!jsDest && !cssDest) {
			throw grunt.util.error('You should specify "dest" and/or "cssDest" properties in your Gruntfile.');
		}

		var includes = ensureArray(this.data.include || []);
		var excludes = ensureArray(this.data.exclude || []);
		var dependencies = this.data.dependencies || {};
		var mains = this.data.mainFiles || {};
		var callback = this.data.callback;
		var process = this.data.process;
		var includeDev = this.data.includeDev === true;
		var bowerOptions = this.data.bowerOptions || {};
		var bowerDir = bowerOptions.relative !== false ? bower.config.cwd : '';
		var options = this.options({
			separator: grunt.util.linefeed
		});

		var done = this.async();
		bowerMainFiles(function(jsFiles, cssFiles) {
			concatenateAndWriteFile(jsFiles, jsDest, options.separator);
			concatenateAndWriteFile(cssFiles, cssDest);
			done();
		});

		/**
		 * Concatenates and writes a file
		 *
		 * @param {Array} files File contents
		 * @param {String} destination File destination
		 * @param {String} separator Files joined on this string
		 */
		function concatenateAndWriteFile(files, destination, separator) {
			if (!destination || !files || !files.length) return;

			files = _.map(files, process);

			var src = files.join(separator || grunt.util.linefeed);
			grunt.file.write(destination, src);
			grunt.log.writeln('File ' + destination.cyan + ' created.');
		}

		/**
		 * Finds suitable JS and CSS files for all installed Bower packages.
		 *
		 * @param {Function} allDone function(bowerFiles) {}
		 */
		function bowerMainFiles(allDone) {
			async.parallel({
				map: bowerList('map'),
				components: bowerList('paths')
			}, function(err, lists) {
				// Ensure all manual defined dependencies are contained in an array
				if (dependencies) {
					_.map(dependencies, function(value, key) {
						dependencies[key] = ensureArray(value);
					});
				}

				// Exclude devDependencies
				var devDependencies = lists.map.pkgMeta.devDependencies;
				if (devDependencies && !includeDev) {
					excludes = excludes.concat(Object.keys(devDependencies));
				}

				// Resolve dependency graph to ensure correct order of components when concat them
				var resolvedDependencies = resolveDependencies(lists.map);

				// List of main files
				var jsFiles = {};
				var jsGroupStats = {};
				var cssGroupStats = {};
				var cssFiles = {};

				_.each(lists.components, function(component, name) {
					if (includes.length && _.indexOf(includes, name) === -1) return;
					if (excludes.length && _.indexOf(excludes, name) !== -1) return;

					var mainFiles = findMainFiles(name, component, lists.map.dependencies[name]);
					if (mainFiles.length) {
						if (callback) mainFiles = callback(mainFiles, name);

						var mainJsFiles = mainFiles.filter(function(file) {
							return isFileExtension(file, '.js');
						});
						var mainCssFiles = mainFiles.filter(function(file) {
							return isFileExtension(file, '.css');
						});

						if (grunt.option('verbose')) {
							jsGroupStats[name]  = mainJsFiles.map(_.partial(toFileStats, name));
							cssGroupStats[name] = mainCssFiles.map(_.partial(toFileStats, name));
						}

						jsFiles[name] = mainJsFiles.map(grunt.file.read);
						cssFiles[name] = mainCssFiles.map(grunt.file.read);
					}
					else {
						// Try to find and concat minispade package: packages/_name_/lib/main.js
						var pkg = getNpmPackage(name, component);
						if (pkg) {
							jsFiles[name] = pkg;
						}
						else {
							grunt.log.error('Can’t detect any .js or .css on main files for "' + name + '" component. ' +
								'You should explicitly define it via bower_concat’s mainFiles option. ' +
								'See Readme for details.'
								);
						}
					}
				});

				if (grunt.option('verbose')) {
					logGroupStats('Scripts', resolvedDependencies, jsDest, jsGroupStats);
					logGroupStats('Styles', resolvedDependencies, cssDest, cssGroupStats);
					grunt.verbose.writeln();
				}

				// Gather files by respecting the order of resolved dependencies
				var jsModules = [];
				var cssModules = [];
				_.each(resolvedDependencies, function(name) {
					if (jsFiles[name]) {
						jsModules = jsModules.concat(jsFiles[name]);
					}
					if (cssFiles[name]) {
						cssModules = cssModules.concat(cssFiles[name]);
					}
				});

				allDone(jsModules, cssModules);
			});
		}

		/**
		 * Returns function that invokes `list` command of Bower API.
		 * Should be used inside async.parallel.
		 *
		 * @param {String} kind map|paths
		 * @return {Function}
		 */
		function bowerList(kind) {
			return function(done) {
				var params = _.extend({}, bowerOptions);
				params[kind] = true;
				bower.commands.list(params, {offline: true})
					.on('error', grunt.fail.fatal.bind(grunt.fail))
					.on('end', function(data) {
						done(null, data);  // null means "no error" for async.parallel
					});
			};
		}

		/**
		 * Builds dependency graph.
		 * See lib/dependencyTools.js.
		 *
		 * @param {Object} map Map from bower.commands.list(kind: map).
		 * @return {Array}
		 */
		function resolveDependencies(map) {
			var dependencyGraph = dependencies || {};
			var resolved = [];
			var unresolved = [];

			// Build dependency graph
			if (map.dependencies) {
				dependencyTools.buildDependencyGraph(
					undefined,  // First recursion without a start value<commit_msg>Passing bowerOptions object to bower.commands.list<commit_after>/**
 * Concatenate installed Bower packages
 *
 * @author <NAME> (http://sapegin.me)
 */


/*jshint node:true */
module.exports = function(grunt) {
	'use strict';

	var path = require('path');
	var fs = require('fs');
	var filesize = require('filesize');
	var bower = require('bower');
	var detective = require('detective');
	var async = require('async');
	var _ = require('lodash');
	_.str = require('underscore.string');
	var dependencyTools = require('../lib/dependencyTools');

	grunt.registerMultiTask('bower_concat', 'Concatenate installed Bower packages.', function() {
		var jsDest = this.data.dest;
		var cssDest = this.data.cssDest;

		// Require at least one of [`dest`, `cssDest`]
		if (!jsDest && !cssDest) {
			throw grunt.util.error('You should specify "dest" and/or "cssDest" properties in your Gruntfile.');
		}

		var includes = ensureArray(this.data.include || []);
		var excludes = ensureArray(this.data.exclude || []);
		var dependencies = this.data.dependencies || {};
		var mains = this.data.mainFiles || {};
		var callback = this.data.callback;
		var process = this.data.process;
		var includeDev = this.data.includeDev === true;
		var bowerOptions = this.data.bowerOptions || {};
		var bowerDir = bowerOptions.relative !== false ? bower.config.cwd : '';
		var options = this.options({
			separator: grunt.util.linefeed
		});

		var done = this.async();
		bowerMainFiles(function(jsFiles, cssFiles) {
			concatenateAndWriteFile(jsFiles, jsDest, options.separator);
			concatenateAndWriteFile(cssFiles, cssDest);
			done();
		});

		/**
		 * Concatenates and writes a file
		 *
		 * @param {Array} files File contents
		 * @param {String} destination File destination
		 * @param {String} separator Files joined on this string
		 */
		function concatenateAndWriteFile(files, destination, separator) {
			if (!destination || !files || !files.length) return;

			files = _.map(files, process);

			var src = files.join(separator || grunt.util.linefeed);
			grunt.file.write(destination, src);
			grunt.log.writeln('File ' + destination.cyan + ' created.');
		}

		/**
		 * Finds suitable JS and CSS files for all installed Bower packages.
		 *
		 * @param {Function} allDone function(bowerFiles) {}
		 */
		function bowerMainFiles(allDone) {
			async.parallel({
				map: bowerList('map'),
				components: bowerList('paths')
			}, function(err, lists) {
				// Ensure all manual defined dependencies are contained in an array
				if (dependencies) {
					_.map(dependencies, function(value, key) {
						dependencies[key] = ensureArray(value);
					});
				}

				// Exclude devDependencies
				var devDependencies = lists.map.pkgMeta.devDependencies;
				if (devDependencies && !includeDev) {
					excludes = excludes.concat(Object.keys(devDependencies));
				}

				// Resolve dependency graph to ensure correct order of components when concat them
				var resolvedDependencies = resolveDependencies(lists.map);

				// List of main files
				var jsFiles = {};
				var jsGroupStats = {};
				var cssGroupStats = {};
				var cssFiles = {};

				_.each(lists.components, function(component, name) {
					if (includes.length && _.indexOf(includes, name) === -1) return;
					if (excludes.length && _.indexOf(excludes, name) !== -1) return;

					var mainFiles = findMainFiles(name, component, lists.map.dependencies[name]);
					if (mainFiles.length) {
						if (callback) mainFiles = callback(mainFiles, name);

						var mainJsFiles = mainFiles.filter(function(file) {
							return isFileExtension(file, '.js');
						});
						var mainCssFiles = mainFiles.filter(function(file) {
							return isFileExtension(file, '.css');
						});

						if (grunt.option('verbose')) {
							jsGroupStats[name]  = mainJsFiles.map(_.partial(toFileStats, name));
							cssGroupStats[name] = mainCssFiles.map(_.partial(toFileStats, name));
						}

						jsFiles[name] = mainJsFiles.map(grunt.file.read);
						cssFiles[name] = mainCssFiles.map(grunt.file.read);
					}
					else {
						// Try to find and concat minispade package: packages/_name_/lib/main.js
						var pkg = getNpmPackage(name, component);
						if (pkg) {
							jsFiles[name] = pkg;
						}
						else {
							grunt.log.error('Can’t detect any .js or .css on main files for "' + name + '" component. ' +
								'You should explicitly define it via bower_concat’s mainFiles option. ' +
								'See Readme for details.'
								);
						}
					}
				});

				if (grunt.option('verbose')) {
					logGroupStats('Scripts', resolvedDependencies, jsDest, jsGroupStats);
					logGroupStats('Styles', resolvedDependencies, cssDest, cssGroupStats);
					grunt.verbose.writeln();
				}

				// Gather files by respecting the order of resolved dependencies
				var jsModules = [];
				var cssModules = [];
				_.each(resolvedDependencies, function(name) {
					if (jsFiles[name]) {
						jsModules = jsModules.concat(jsFiles[name]);
					}
					if (cssFiles[name]) {
						cssModules = cssModules.concat(cssFiles[name]);
					}
				});

				allDone(jsModules, cssModules);
			});
		}

		/**
		 * Returns function that invokes `list` command of Bower API.
		 * Should be used inside async.parallel.
		 *
		 * @param {String} kind map|paths
		 * @return {Function}
		 */
		function bowerList(kind) {
			return function(done) {
				var params = _.extend({}, bowerOptions);
				params[kind] = true;
				bower.commands.list(params, {offline: true}, bowerOptions)
					.on('error', grunt.fail.fatal.bind(grunt.fail))
					.on('end', function(data) {
						done(null, data);  // null means "no error" for async.parallel
					});
			};
		}

		/**
		 * Builds dependency graph.
		 * See lib/dependencyTools.js.
		 *
		 * @param {Object} map Map from bower.commands.list(kind: map).
		 * @return {Array}
		 */
		function resolveDependencies(map) {
			var dependencyGraph = dependencies || {};
			var resolved = [];
			var unresolved = [];

			// Build dependency graph
			if (map.dependencies) {
				dependencyTools.buildDependencyGraph(
					undefined,  // First recursion without a start value
{'fraction_non_alphanumeric': 0.13807531380753138, 'fraction_numerical': 0.0003281647386988268, 'mean_word_length': 3.223839223839224, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5357392', 'n_tokens_mistral': 4619, 'n_tokens_neox': 3638, 'n_words': 906}