jquery-ui/grunt.js
2012-03-23 13:33:46 -04:00

551 lines
17 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

module.exports = function( grunt ) {
function stripBanner( files ) {
return files.map(function( file ) {
return "<strip_all_banners:" + file + ">";
});
}
function stripDirectory( file ) {
return file.replace( /.+\/(.+)$/, "$1" );
}
function createBanner( files ) {
// strip folders
var fileNames = files && files.map( stripDirectory );
return "/*! <%= pkg.title || pkg.name %> - v<%= pkg.version %> - " +
"<%= grunt.template.today('isoDate') %>\n" +
"<%= pkg.homepage ? '* ' + pkg.homepage + '\n' : '' %>" +
"* Includes: " + (files ? fileNames.join(", ") : "<%= stripDirectory(grunt.task.current.file.src[1]) %>") + "\n" +
"* Copyright (c) <%= grunt.template.today('yyyy') %> <%= pkg.author.name %>;" +
" Licensed <%= _.pluck(pkg.licenses, 'type').join(', ') %> */";
}
// allow access from banner template
global.stripDirectory = stripDirectory;
grunt.registerHelper( "strip_all_banners", function( filepath ) {
return grunt.file.read( filepath ).replace( /^\s*\/\*[\s\S]*?\*\/\s*/g, "" );
});
var inspect = require( "util" ).inspect;
var coreFiles = "jquery.ui.core.js, jquery.ui.widget.js, jquery.ui.mouse.js, jquery.ui.draggable.js, jquery.ui.droppable.js, jquery.ui.resizable.js, jquery.ui.selectable.js, jquery.ui.sortable.js, jquery.effects.core.js".split( ", " );
var uiFiles = coreFiles.map(function( file ) {
return "ui/" + file;
}).concat( grunt.file.expandFiles( "ui/*.js" ).filter(function( file ) {
return coreFiles.indexOf( file.substring(3) ) === -1;
}));
var minify = {
"dist/jquery-ui.min.js": [ "<banner:meta.bannerAll>", "dist/jquery-ui.js" ],
"dist/i18n/jquery-ui-i18n.min.js": [ "<banner:meta.bannerI18n>", "dist/i18n/jquery-ui-i18n.js" ]
};
function minFile( file ) {
minify[ "dist/" + file.replace( /\.js$/, ".min.js" ).replace( /ui\//, "minified/" ) ] = [ "<banner>", file ];
}
uiFiles.forEach( minFile );
var allI18nFiles = grunt.file.expandFiles( "ui/i18n/*.js" );
allI18nFiles.forEach( minFile );
var cssFiles = "core accordion autocomplete button datepicker dialog menu progressbar resizable selectable slider spinner tabs tooltip theme".split( " " ).map(function( component ) {
return "themes/base/jquery.ui." + component + ".css";
});
var minifyCSS = {
"dist/jquery-ui.min.css": "dist/jquery-ui.css"
};
cssFiles.forEach(function( file ) {
minifyCSS[ "dist/" + file.replace( /\.css$/, ".min.css" ).replace( /themes\/base\//, "themes/base/minified/" ) ] = [ "<banner>", file ];
});
grunt.initConfig({
pkg: "<json:package.json>",
files: {
dist: "<%= pkg.name %>-<%= pkg.version %>",
cdn: "<%= pkg.name %>-<%= pkg.version %>-cdn",
themes: "<%= pkg.name %>-themes-<%= pkg.version %>"
},
meta: {
banner: createBanner(),
bannerAll: createBanner( uiFiles ),
bannerI18n: createBanner( allI18nFiles ),
bannerCSS: createBanner( cssFiles )
},
compare_size: {
files: [
"dist/jquery-ui.js",
"dist/jquery-ui.min.js"
]
},
concat: {
ui: {
src: [ "<banner:meta.bannerAll>", stripBanner( uiFiles ) ],
dest: "dist/jquery-ui.js"
},
i18n: {
src: [ "<banner:meta.bannerI18n>", allI18nFiles ],
dest: "dist/i18n/jquery-ui-i18n.js"
},
css: {
src: [ "<banner:meta.bannerCSS>", stripBanner( cssFiles ) ],
dest: "dist/jquery-ui.css"
}
},
min: minify,
css_min: minifyCSS,
copy: {
dist: {
src: [
"AUTHORS.txt",
"GPL-LICENSE.txt",
"jquery-1.7.1.js",
"MIT-LICENSE.txt",
"README.md",
"grunt.js",
"package.json",
"ui/**/*",
"demos/**/*",
"themes/**/*",
"external/**/*",
"tests/**/*"
],
renames: {
"dist/jquery-ui.js": "ui/jquery-ui.js",
"dist/jquery-ui.min.js": "ui/minified/jquery-ui.min.js",
"dist/i18n/jquery-ui-i18n.js": "ui/i18n/jquery-ui-i18n.js",
"dist/i18n/jquery-ui-i18n.min.js": "ui/minified/i18n/jquery-ui-i18n.min.js",
"dist/jquery-ui.css": "themes/base/jquery-ui.css",
"dist/jquery-ui.min.css": "themes/base/minified/jquery-ui.min.css"
},
dest: "dist/<%= files.dist %>"
},
dist_min: {
src: "dist/minified/**/*",
strip: /^dist/,
dest: "dist/<%= files.dist %>/ui"
},
dist_css_min: {
src: "dist/themes/base/minified/*.css",
strip: /^dist/,
dest: "dist/<%= files.dist %>"
},
dist_min_images: {
src: "themes/base/images/*",
strip: /^themes\/base\//,
dest: "dist/<%= files.dist %>/themes/base/minified"
},
cdn: {
src: [
"AUTHORS.txt",
"GPL-LICENSE.txt",
"MIT-LICENSE.txt",
"ui/*.js",
"package.json"
],
renames: {
"dist/jquery-ui.js": "jquery-ui.js",
"dist/jquery-ui.min.js": "jquery-ui.min.js",
"dist/i18n/jquery-ui-i18n.js": "i18n/jquery-ui-i18n.js",
"dist/i18n/jquery-ui-i18n.min.js": "i18n/jquery-ui-i18n.min.js",
"dist/jquery-ui.css": "themes/base/jquery-ui.css",
"dist/jquery-ui.min.css": "themes/base/minified/jquery-ui.min.css"
},
dest: "dist/<%= files.cdn %>"
},
cdn_i18n: {
src: "ui/i18n/jquery.ui.datepicker-*.js",
strip: "ui/",
dest: "dist/<%= files.cdn %>"
},
cdn_i18n_min: {
src: "dist/minified/i18n/jquery.ui.datepicker-*.js",
strip: "dist/minified",
dest: "dist/<%= files.cdn %>"
},
cdn_min: {
src: "dist/minified/*.js",
strip: /^dist\/minified/,
dest: "dist/<%= files.cdn %>/ui"
},
cdn_min_images: {
src: "themes/base/images/*",
strip: /^themes\/base\//,
dest: "dist/<%= files.cdn %>/themes/base/minified"
},
cdn_themes: {
src: "dist/<%= files.themes %>/themes/**/*",
strip: "dist/<%= files.themes %>",
dest: "dist/<%= files.cdn %>"
},
themes: {
src: [
"AUTHORS.txt",
"GPL-LICENSE.txt",
"MIT-LICENSE.txt",
"package.json"
],
dest: "dist/<%= files.themes %>"
}
},
zip: {
dist: {
src: "<%= files.dist %>",
dest: "<%= files.dist %>.zip"
},
cdn: {
src: "<%= files.cdn %>",
dest: "<%= files.cdn %>.zip"
},
themes: {
src: "<%= files.themes %>",
dest: "<%= files.themes %>.zip"
}
},
md5: {
dist: {
src: "dist/<%= files.dist %>",
dest: "dist/<%= files.dist %>/MANIFEST"
},
cdn: {
src: "dist/<%= files.cdn %>",
dest: "dist/<%= files.cdn %>/MANIFEST"
},
themes: {
src: "dist/<%= files.themes %>",
dest: "dist/<%= files.themes %>/MANIFEST"
}
},
qunit: {
files: grunt.file.expandFiles( "tests/unit/**/*.html" ).filter(function( file ) {
// disabling everything that doesn't (quite) work with PhantomJS for now
// except for all|index|test, try to include more as we go
return !( /(all|index|test|draggable|droppable|selectable|resizable|sortable|dialog|slider|datepicker|tabs|tabs_deprecated)\.html/ ).test( file );
})
},
lint: {
ui: "ui/*",
grunt: "grunt.js",
tests: "tests/unit/**/*.js"
},
csslint: {
base_theme: {
src: "themes/base/*.css",
rules: {
"import": false,
"overqualified-elements": 2
}
}
},
jshint: {
options: {
curly: true,
eqeqeq: true,
immed: true,
latedef: true,
newcap: true,
noarg: true,
sub: true,
undef: true,
eqnull: true
},
grunt: {
options: {
node: true
},
globals: {
task: true,
config: true,
file: true,
log: true,
template: true
}
},
ui: {
options: {
browser: true
},
globals: {
jQuery: true
}
},
tests: {
options: {
jquery: true
},
globals: {
module: true,
test: true,
ok: true,
equal: true,
deepEqual: true,
QUnit: true
}
}
}
});
grunt.registerMultiTask( "copy", "Copy files to destination folder and replace @VERSION with pkg.version", function() {
function replaceVersion( source ) {
return source.replace( "@VERSION", grunt.config( "pkg.version" ) );
}
var files = grunt.file.expandFiles( this.file.src );
var target = this.file.dest + "/";
var strip = this.data.strip;
if ( typeof strip === "string" ) {
strip = new RegExp( "^" + grunt.template.process( strip, grunt.config() ).replace( /[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&" ) );
}
files.forEach(function( fileName ) {
var targetFile = strip ? fileName.replace( strip, "" ) : fileName;
if ( /png$/.test( fileName ) ) {
grunt.file.copy( fileName, target + targetFile );
} else {
grunt.file.copy( fileName, target + targetFile, replaceVersion );
}
});
grunt.log.writeln( "Copied " + files.length + " files." );
var renameCount = 0;
for ( var fileName in this.data.renames ) {
renameCount += 1;
grunt.file.copy( fileName, target + grunt.template.process( this.data.renames[ fileName ], grunt.config() ) );
}
if ( renameCount ) {
grunt.log.writeln( "Renamed " + renameCount + " files." );
}
});
grunt.registerMultiTask( "zip", "Create a zip file for release", function() {
// TODO switch back to adm-zip for better cross-platform compability once it actually works
// 0.1.3 works, but result can't be unzipped
// its also a lot slower then zip program, probably due to how its used...
// var files = grunt.file.expandFiles( "dist/" + this.file.src + "/**/*" );
// grunt.log.writeln( "Creating zip file " + this.file.dest );
// var fs = require( "fs" );
// var AdmZip = require( "adm-zip" );
// var zip = new AdmZip();
// files.forEach(function( file ) {
// grunt.verbose.writeln( "Zipping " + file );
// // rewrite file names from dist folder (created by build), drop the /dist part
// zip.addFile(file.replace(/^dist/, "" ), fs.readFileSync( file ) );
// });
// zip.writeZip( "dist/" + this.file.dest );
// grunt.log.writeln( "Wrote " + files.length + " files to " + this.file.dest );
var done = this.async();
var dest = this.file.dest;
var src = grunt.template.process( this.file.src, grunt.config() );
grunt.utils.spawn({
cmd: "zip",
args: [ "-r", dest, src ],
opts: {
cwd: 'dist'
}
}, function( err, result ) {
if ( err ) {
grunt.log.error( err );
done();
return;
}
grunt.log.writeln( "Zipped " + dest );
done();
});
});
grunt.registerMultiTask( "csslint", "Lint CSS files with csslint", function() {
var csslint = require( "csslint" ).CSSLint;
var files = grunt.file.expandFiles( this.file.src );
var ruleset = {};
csslint.getRules().forEach(function( rule ) {
ruleset[ rule.id ] = 1;
});
for ( var rule in this.data.rules ) {
if ( !this.data.rules[ rule ] ) {
delete ruleset[rule];
} else {
ruleset[ rule ] = this.data.rules[ rule ];
}
}
var hadErrors = 0;
files.forEach(function( filepath ) {
grunt.log.writeln( "Linting " + filepath );
var result = csslint.verify( grunt.file.read( filepath ), ruleset );
result.messages.forEach(function( message ) {
grunt.log.writeln( "[".red + ( "L" + message.line ).yellow + ":".red + ( "C" + message.col ).yellow + "]".red );
log[ message.type === "error" ? "error" : "writeln" ]( message.message + " " + message.rule.desc + " (" + message.rule.id + ")" );
});
if ( result.messages.length ) {
hadErrors += 1;
}
});
if (hadErrors) {
return false;
}
grunt.log.writeln( "Lint free" );
});
grunt.registerMultiTask( "css_min", "Minify CSS files with Sqwish.", function() {
var max = grunt.helper( "concat", grunt.file.expandFiles( this.file.src ) );
var min = require( "sqwish" ).minify( max, false );
grunt.file.write( this.file.dest, min );
grunt.log.writeln( "File '" + this.file.dest + "' created." );
grunt.helper( "min_max_info", min, max );
});
grunt.registerMultiTask( "md5", "Create list of md5 hashes for CDN uploads", function() {
// remove dest file before creating it, to make sure itself is not included
if ( require( "path" ).existsSync( this.file.dest ) ) {
require( "fs" ).unlinkSync( this.file.dest );
}
var crypto = require( "crypto" );
var dir = this.file.src + "/";
var hashes = [];
grunt.file.expandFiles( dir + "**/*" ).forEach(function( fileName ) {
var hash = crypto.createHash( "md5" );
hash.update( grunt.file.read( fileName, "ascii" ) );
hashes.push( fileName.replace( dir, "" ) + " " + hash.digest( "hex" ) );
});
grunt.file.write( this.file.dest, hashes.join( "\n" ) + "\n" );
grunt.log.writeln( "Wrote " + this.file.dest + " with " + hashes.length + " hashes" );
});
grunt.registerTask( "download_themes", function() {
// var AdmZip = require('adm-zip');
var fs = require( "fs" );
var request = require( "request" );
var done = this.async();
var themes = grunt.file.read( "build/themes" ).split(",");
var requests = 0;
grunt.file.mkdir( "dist/tmp" );
themes.forEach(function( theme, index ) {
requests += 1;
grunt.file.mkdir( "dist/tmp/" + index );
var zipFileName = "dist/tmp/" + index + ".zip";
var out = fs.createWriteStream( zipFileName );
out.on( "close", function() {
grunt.log.writeln( "done downloading " + zipFileName );
// TODO AdmZip produces "crc32 checksum failed", need to figure out why
// var zip = new AdmZip(zipFileName);
// zip.extractAllTo('dist/tmp/' + index + '/');
// until then, using cli unzip...
grunt.utils.spawn({
cmd: "unzip",
args: [ "-d", "dist/tmp/" + index, zipFileName ]
}, function( err, result ) {
grunt.log.writeln( "Unzipped " + zipFileName + ", deleting it now" );
fs.unlinkSync( zipFileName );
requests -= 1;
if (requests === 0) {
done();
}
});
});
request( "http://ui-dev.jquery.com/download/?" + theme ).pipe( out );
});
});
grunt.registerTask( "copy_themes", function() {
// each package includes the base theme, ignore that
var filter = /themes\/base/;
var files = grunt.file.expandFiles( "dist/tmp/*/development-bundle/themes/**/*" ).filter(function( file ) {
return !filter.test( file );
});
// TODO the grunt.template.process call shouldn't be necessary
var target = "dist/" + grunt.template.process( grunt.config( "files.themes" ), grunt.config() ) + "/";
files.forEach(function( fileName ) {
var targetFile = fileName.replace( /dist\/tmp\/\d+\/development-bundle\//, "" ).replace( "jquery-ui-.custom", "jquery-ui" );
grunt.file.copy( fileName, target + targetFile );
});
// copy minified base theme from regular release
// TODO same as the one above
var distFolder = "dist/" + grunt.template.process( grunt.config( "files.dist" ), grunt.config() );
files = grunt.file.expandFiles( distFolder + "/themes/base/**/*" );
files.forEach(function( fileName ) {
grunt.file.copy( fileName, target + fileName.replace( distFolder, "" ) );
});
});
grunt.registerTask( "clean", function() {
require( "rimraf" ).sync( "dist" );
});
// TODO merge with code in jQuery Core, share as grunt plugin/npm
// this here actually uses the provided filenames in the output
// the helpers should just be regular functions, no need to share those with the world
grunt.registerMultiTask( "compare_size", "Compare size of this branch to master", function() {
var files = grunt.file.expandFiles( this.file.src ),
done = this.async(),
sizecache = __dirname + "/dist/.sizecache.json",
sources = {
min: grunt.file.read( files[1] ),
max: grunt.file.read( files[0] )
},
oldsizes = {},
sizes = {};
try {
oldsizes = JSON.parse( grunt.file.read( sizecache ) );
} catch( e ) {
oldsizes = {};
}
// Obtain the current branch and continue...
grunt.helper( "git_current_branch", function( err, branch ) {
var key, diff;
// Derived and adapted from Corey Frang's original `sizer`
grunt.log.writeln( "sizes - compared to master" );
sizes[ files[0] ] = sources.max.length;
sizes[ files[1] ] = sources.min.length;
sizes[ files[1] + ".gz" ] = grunt.helper( "gzip", sources.min ).length;
for ( key in sizes ) {
diff = oldsizes[ key ] && ( sizes[ key ] - oldsizes[ key ] );
if ( diff > 0 ) {
diff = "+" + diff;
}
console.log( "%s %s %s",
grunt.helper("lpad", sizes[ key ], 8 ),
grunt.helper("lpad", diff ? "(" + diff + ")" : "(-)", 8 ),
key
);
}
if ( branch === "master" ) {
// If master, write to file - this makes it easier to compare
// the size of your current code state to the master branch,
// without returning to the master to reset the cache
grunt.file.write( sizecache, JSON.stringify(sizes) );
}
done();
});
});
grunt.registerHelper( "git_current_branch", function( done ) {
grunt.utils.spawn({
cmd: "git",
args: [ "branch", "--no-color" ]
}, function( err, result ) {
var branch;
result.split( "\n" ).forEach(function( branch ) {
var matches = /^\* (.*)/.exec( branch );
if ( matches !== null && matches.length && matches[ 1 ] ) {
done( null, matches[ 1 ] );
}
});
});
});
grunt.registerHelper( "lpad", function( str, len, chr ) {
return ( Array( len + 1 ).join( chr || " " ) + str ).substr( -len );
});
grunt.registerTask( "default", "lint csslint qunit build compare_size" );
grunt.registerTask( "sizer", "concat:ui min:dist/jquery-ui.min.js compare_size" );
grunt.registerTask( "build", "concat min css_min" );
grunt.registerTask( "release", "clean build copy:dist copy:dist_min copy:dist_min_images copy:dist_css_min md5:dist zip:dist" );
grunt.registerTask( "release_themes", "release download_themes copy_themes copy:themes md5:themes zip:themes" );
grunt.registerTask( "release_cdn", "release_themes copy:cdn copy:cdn_min copy:cdn_i18n copy:cdn_i18n_min copy:cdn_min_images copy:cdn_themes md5:cdn zip:cdn" );
};