4 * https://gist.github.com/2489540
8 /*global config:true, task:true*/
9 module.exports = function( grunt ) {
13 // https://gist.github.com/2876125
14 function readOptionalJSON( filepath ) {
17 data = grunt.file.readJSON(filepath);
18 grunt.log.write( "Reading data from " + filepath + "..." ).ok();
23 var task = grunt.task;
24 var file = grunt.file;
25 var utils = grunt.utils;
27 var verbose = grunt.verbose;
28 var fail = grunt.fail;
29 var option = grunt.option;
30 var config = grunt.config;
31 var template = grunt.template;
38 pkg: "<json:package.json>",
39 dst: readOptionalJSON("dist/.destination.json"),
41 banner: "/*! jQuery v@<%= pkg.version %> jquery.com | jquery.org/license */"
48 "src/sizzle-jquery.js",
49 "src/sizzle/sizzle.js"
65 "src/manipulation.js",
66 { flag: "css", src: "src/css.js" },
67 { flag: "ajax", src: "src/ajax.js" },
68 { flag: "ajax/jsonp", src: "src/ajax/jsonp.js", needs: [ "ajax", "ajax/script" ] },
69 { flag: "ajax/script", src: "src/ajax/script.js", needs: ["ajax"] },
70 { flag: "ajax/xhr", src: "src/ajax/xhr.js", needs: ["ajax"] },
71 { flag: "effects", src: "src/effects.js", needs: ["css"] },
72 { flag: "offset", src: "src/offset.js", needs: ["css"] },
73 { flag: "dimensions", src: "src/dimensions.js", needs: ["css"] },
79 "dist/jquery.min.js": [ "<banner>", "dist/jquery.js" ]
82 files: [ "grunt.js", "dist/jquery.js" ]
85 files: "test/index.html"
88 files: [ "<config:lint.files>", "src/**/*.js" ],
124 grunt.registerTask( "default", "submodules selector build:*:* lint min dist:* compare_size" );
126 // Short list as a high frequency watch task
127 grunt.registerTask( "dev", "selector build:*:* lint" );
129 // Load the "compare_size" task from NPM packages
130 grunt.loadNpmTasks("grunt-compare-size");
132 grunt.registerTask( "testswarm", function( commit, configFile ) {
133 var testswarm = require( "testswarm" ),
135 config = grunt.file.readJSON( configFile ).jquery;
136 var tests = "ajax attributes callbacks core css data deferred dimensions effects event manipulation offset queue selector support traversing".split( " " );
137 tests.forEach(function( test ) {
138 testUrls.push( config.testUrl + commit + "/test/index.html?module=" + test );
141 url: config.swarmUrl,
143 timeout: 1000 * 60 * 30,
146 authUsername: config.authUsername,
147 authToken: config.authToken,
148 jobName: 'jQuery commit #<a href="https://github.com/jquery/jquery/commit/' + commit + '">' + commit.substr( 0, 10 ) + '</a>',
149 runMax: config.runMax,
151 "runUrls[]": testUrls,
152 "browserSets[]": ["popular"]
156 // Build src/selector.js
157 grunt.registerMultiTask( "selector", "Build src/selector.js", function() {
159 var name = this.file.dest,
160 files = this.file.src,
162 api: file.read( files[0] ),
163 src: file.read( files[1] )
167 // sizzle-jquery.js -> sizzle after "EXPOSE", replace window.Sizzle
168 compiled = sizzle.src.replace( "window.Sizzle = Sizzle;", sizzle.api );
169 verbose.write("Injected sizzle-jquery.js into sizzle.js");
171 // Write concatenated source to file
172 file.write( name, compiled );
174 // Fail task if errors were logged.
175 if ( this.errorCount ) {
179 // Otherwise, print a success message.
180 log.writeln( "File '" + name + "' created." );
184 // Special "alias" task to make custom build creation less grawlix-y
185 grunt.registerTask( "custom", function() {
186 var done = this.async(),
187 args = [].slice.call(arguments),
188 modules = args.length ? args[0].replace(/,/g, ":") : "";
191 // Translation example
193 // grunt custom:+ajax,-dimensions,-effects,-offset
197 // grunt build:*:*:-ajax:-dimensions:-effects:-offset
199 grunt.log.writeln( "Creating custom build...\n" );
203 args: [ "build:*:*:" + modules ]
204 }, function( err, result ) {
206 grunt.verbose.error();
211 grunt.log.writeln( result.replace("Done, without errors.", "") );
217 // Special concat/build task to handle various jQuery build requirements
219 grunt.registerMultiTask(
221 "Concatenate source (include/exclude modules with +/- flags), embed date/version",
223 // Concat specified files.
226 modules = this.flags,
227 explicit = Object.keys(modules).length > 1,
228 optIn = !modules["*"],
229 name = this.file.dest,
231 excluder = function( flag, needsFlag ) {
232 // explicit > implicit, so set this first and let it be overridden by explicit
233 if ( optIn && !modules[ flag ] && !modules[ "+" + flag ] ) {
234 excluded[ flag ] = false;
237 if ( excluded[ needsFlag ] || modules[ "-" + flag ] ) {
238 // explicit exclusion from flag or dependency
239 excluded[ flag ] = true;
240 } else if ( modules[ "+" + flag ] && ( excluded[ needsFlag ] === false ) ) {
241 // explicit inclusion from flag or dependency overriding a weak inclusion
242 delete excluded[ needsFlag ];
247 // figure out which files to exclude based on these rules in this order:
248 // explicit > implicit (explicit also means a dependency/dependent that was explicit)
251 // *: none (implicit exclude)
252 // *:* all (implicit include)
253 // *:*:-effects all except effects (explicit > implicit)
254 // *:*:-css all except css and its deps (explicit)
255 // *:*:-css:+effects all except css and its deps (explicit exclude from dep. trumps explicit include)
256 // *:+effects none except effects and its deps (explicit include from dep. trumps implicit exclude)
257 this.file.src.forEach(function( filepath ) {
258 var flag = filepath.flag;
264 // check for dependencies
265 if ( filepath.needs ) {
266 filepath.needs.forEach(function( needsFlag ) {
267 excluder( flag, needsFlag );
273 // conditionally concatenate source
274 this.file.src.forEach(function( filepath ) {
275 var flag = filepath.flag,
280 if ( excluded[ flag ] !== undefined ) {
281 message = ( "Excluding " + flag ).red;
284 message = ( "Including " + flag ).green;
286 // If this module was actually specified by the
287 // builder, then st the flag to include it in the
289 if ( modules[ "+" + flag ] ) {
294 // Only display the inclusion/exclusion list when handling
297 // Additionally, only display modules that have been specified
299 if ( explicit && specified ) {
300 grunt.log.writetableln([ 27, 30 ], [
302 ( "(" + filepath.src + ")").grey
306 filepath = filepath.src;
309 compiled += file.read( filepath );
314 compiled = compiled.replace( "@DATE", new Date() )
315 .replace( "@VERSION", config("pkg.version") );
317 // Write concatenated source to file
318 file.write( name, compiled );
320 // Fail task if errors were logged.
321 if ( this.errorCount ) {
325 // Otherwise, print a success message.
326 log.writeln( "File '" + name + "' created." );
329 grunt.registerTask( "submodules", function() {
330 var done = this.async();
332 grunt.verbose.write( "Updating submodules..." );
334 // TODO: migrate remaining `make` to grunt tasks
338 }, function( err, result ) {
340 grunt.verbose.error();
345 grunt.log.writeln( result );
351 // Allow custom dist file locations
352 grunt.registerTask( "dist", function() {
353 var flags, paths, stored;
355 // Check for stored destination paths
356 // ( set in dist/.destination.json )
357 stored = Object.keys( config("dst") );
359 // Allow command line input as well
360 flags = Object.keys( this.flags );
362 // Combine all output target paths
363 paths = [].concat( stored, flags ).filter(function( path ) {
368 // Proceed only if there are actual
370 if ( paths.length ) {
372 // 'distpaths' is declared at the top of the
373 // module.exports function scope. It is an array
374 // of default files that jQuery creates
375 distpaths.forEach(function( filename ) {
376 paths.forEach(function( path ) {
379 if ( !/\/$/.test( path ) ) {
383 created = path + filename.replace( "dist/", "" );
385 if ( !/^\//.test( path ) ) {
386 log.error( "File '" + created + "' was NOT created." );
390 file.write( created, file.read(filename) );
392 log.writeln( "File '" + created + "' created." );