Skip to content

Instantly share code, notes, and snippets.

@dpashkevich
Created October 8, 2012 10:24
Show Gist options
  • Save dpashkevich/3851841 to your computer and use it in GitHub Desktop.
Save dpashkevich/3851841 to your computer and use it in GitHub Desktop.
Grunt setup for a big project
[
{
"name": "SubModule",
"src": [
"SubModule/css/Sub.1.css",
"SubModule/css/Sub.2.css",
"SubModule/css/Sub.3.css"
],
"dest": "SubModule/css/submodule-all.css"
}, {
"name": "app",
"src": [
"css/style.css"
],
"dest": "css/awesome-project-all.css"
}
]
Directory structure
SubModule/
css/
...
js/
...
images/
...
ExtJS/
css/
ext-all.css
js/
ext-all.js
ext-all-dev.js
overrides.js
images/
...
css/
...
js/
...
images/
...
awesome-project.json
jsfiles.json
cssfiles.json
index.tmpl
grunt.js
/*global module:false*/
module.exports = function(grunt) {
var
// Note: pre-minified static resources (ExtJS) will be added before all project files
jsBundle = grunt.file.readJSON('jsfiles.json'),
cssBundle = grunt.file.readJSON('cssfiles.json'),
// flat array of all source js files
jsFiles = [],
// flat array of all source css files
cssFiles = [];
// create flat arrays of all source files
(function processBundles() {
var i, len;
for(i = 0, len = jsBundle.length; i < len; i++) {
// append to flat array of all source files
jsFiles = jsFiles.concat(jsBundle[i].src);
}
for(i = 0, len = cssBundle.length; i < len; i++) {
// append to flat array of all source files
cssFiles = cssFiles.concat(cssBundle[i].src);
}
})();
// Import npm tasks
grunt.loadNpmTasks('grunt-contrib-clean');
grunt.loadNpmTasks('grunt-contrib-mincss');
// Import custom tasks
grunt.loadTasks('build/tasks');
// Project configuration.
grunt.initConfig({
pkg: '<json:awesome-project.json>',
meta: {
banner: '/*! <%= pkg.title || pkg.name %> - v<%= pkg.version %> - ' +
'<%= grunt.template.today("yyyy-mm-dd") %>\n' +
'<%= pkg.homepage ? "* " + pkg.homepage + "\n" : "" %>' +
'* Copyright (c) <%= grunt.template.today("yyyy") %> ' +
'Pickles&Cows Ltd. */'
},
// ExtJS development js
extJsDevFile: 'ExtJS/js/ext-all-dev.js',
// ExtJS production js
extJsProdFile: 'ExtJS/js/ext-all.js',
// ExtJS development css file
extJsDevCss: 'ExtJS/css/ext-all.css',
// ExtJS production css file
extJsProdCss: 'ExtJS/css/ext-all.css',
// Project files definitions
jsBundle: jsBundle,
cssBundle: cssBundle,
jsFiles: jsFiles,
cssFiles: cssFiles,
lint: {
files: ['grunt.js'].concat(jsFiles)
},
concat: {
// config generated dynamically
},
// index.html builder task options
index: {
src: 'index.tmpl'
/**
* These properties are set dynamically depending on build configuration:
* dest - destination index.html file path
* cssFiles - all css files
* jsFiles - all js files
*/
},
// file copier
copy: {
/* copy static resources for production build */
dist: {
src: ['images/**/*', 'fonts/**/*', 'ExtJS/images/**/*', 'SubModule/images/**/*'],
dest: 'dist/'
}
},
// js minifier (UglifyJs)
min: {
// config generated dynamically
},
// css minifier
mincss: {
// config generated dynamically
},
// clean up temporary files left after build process (or entire build dir)
clean: {
dist_entire: 'dist'
}
});
grunt.registerTask('prod', 'Production build', function() {
var
path = require('path'),
jsBundle = grunt.config('jsBundle'),
cssBundle = grunt.config('cssBundle'),
// TODO don't hardcode
outDir = 'dist/',
// target files that need to be included in index.html
jsTargets = [],
cssTargets = [],
// task queue...
tasks = [];
/**
* Iterates over package definitions in a bundle,
* generating tasks for concatenating and minifying all files defined in each package.
*
* @param {Object} o Options object
* @param {Array} o.bundle The bundle to process
* @param {Array} o.tasks Task queue array to push into
* @param {String} o.minTask Task to use for minification
* @param {String} o.taskPrefix A prefix to add to generated task names
* @param {Array} o.targets Array of target files to push into (paths relative to output directory)
* @param {String} o.outDir Output directory for targets
*/
function processBundle(o) {
var bundle = o.bundle,
tasks = o.tasks,
minTask = o.minTask,
taskPrefix = o.taskPrefix,
targets = o.targets,
outDir = o.outDir,
pkg,
dest,
taskName;
for(var i = 0, len = bundle.length; i < len; i++) {
pkg = bundle[i];
// add target file path without the destination directory
targets.push(pkg.dest);
dest = outDir + pkg.dest;
// defining concatenation task for the package
taskName = taskPrefix + pkg.name;
grunt.config('concat.' + taskName, {
src: pkg.src,
dest: dest
});
// add the task we defined to the queue
tasks.push('concat:' + taskName);
// defining minification task
grunt.config(minTask + '.' + taskName, {
// now we're using the same path as src and dest...
// dunno if that's a good idea but at least we don't have garbage files to deal with
src: dest,
dest: dest
});
tasks.push(minTask + ':' + taskName);
}
}
// processing js bundle...
processBundle({
bundle: jsBundle,
tasks: tasks,
minTask: 'min',
taskPrefix: 'js_',
targets: jsTargets,
outDir: outDir
});
// defining final js concatenation task (prepend banner and statics to the first package in js bundle)
grunt.config('concat.js_final', {
src: [
grunt.config('extJsProdFile'),
'\n\n<banner:meta.banner>',
outDir + jsBundle[0].dest
],
dest: outDir + jsBundle[0].dest
});
tasks.push('concat:js_final');
// processing css bundle...
processBundle({
bundle: cssBundle,
tasks: tasks,
minTask: 'mincss',
taskPrefix: 'css_',
targets: cssTargets,
outDir: outDir
});
// prepend statics
cssTargets.unshift(grunt.config('extJsProdCss'));
// copy lib css
grunt.config('copy.prod', {
src: grunt.config('extJsProdCss'),
dest: 'dist/'
});
// override data for generating index.html
grunt.config('index.dest', outDir + 'index.html');
grunt.config('index.jsFiles', jsTargets);
grunt.config('index.cssFiles', cssTargets);
tasks = [].concat(
// Nuke the entire build directory first...
'clean:dist_entire',
// Tasks for building JS & CSS
tasks,
// Copy static resources
'copy:dist',
'copy:prod',
// Generate index.html
'index'
);
// finally run tasks in specified order
grunt.task.run(tasks);
grunt.log.writeln('Production build complete');
});
};
module.exports = function( grunt ) {
grunt.registerTask( "index", "Generate index.html depending on configuration", function() {
var conf = grunt.config('index'),
jsFiles = conf.jsFiles,
cssFiles = conf.cssFiles,
tpl = grunt.file.read(conf.src),
tplData = {},
cssIncludes,
jsIncludes,
targetFile;
tplData.title = grunt.config('pkg.title');
/*********** COLLECTING CSS ***********/
grunt.log.writeln('Collecting CSS...');
if(cssFiles && cssFiles.length) {
cssIncludes = cssFiles.map(function(v) {
return '<link rel="stylesheet" type="text/css" href="' + v + '" />\n';
}).join('');
}
tplData.css = cssIncludes;
/*********** COLLECTING JAVASCRIPT ***********/
grunt.log.writeln('Collecting JavaScript...');
if(jsFiles && jsFiles.length) {
jsIncludes = jsFiles.map(function(v) {
return '<script type="text/javascript" src="' + v + '"></script>\n';
}).join('');
}
tplData.js = jsIncludes;
/*********** WRITING TEMPLATE ***********/
grunt.log.writeln('Finally writing the template...');
targetFile = conf.dest;
grunt.file.write(targetFile, grunt.template.process(tpl, tplData));
grunt.log.writeln('Generated \'' + targetFile + '\' from \'' + conf.src + '\'');
});
}
<!doctype html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="google" value="notranslate" />
<title><%= title %></title>
<link rel="shortcut icon" href="favicon.ico" />
<%= css %>
<%= js %>
</head>
</html>
[
{
"name": "app",
"src": [
"ExtJS/js/overrides.js",
"SubModule/js/Sub.1.js",
"SubModule/js/Sub.2.js",
"SubModule/js/Sub.3.js",
"js/debug.js",
"js/common.js",
"js/data.js",
"js/app.js"
],
"dest": "js/awesome-project-all.js"
}
]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment