1 module.exports = function( grunt ) {
10 readOptionalJSON = function( filepath ) {
13 data = grunt.file.readJSON( filepath );
19 pkg: grunt.file.readJSON("package.json"),
20 dst: readOptionalJSON("dist/.destination.json"),
25 destFile: "src/selector-sizzle.js",
26 apiFile: "src/sizzle-jquery.js",
27 srcFile: "src/sizzle/sizzle.js"
31 dest: "dist/jquery.js",
42 { flag: "sizzle", src: "src/selector-sizzle.js", alt: "src/selector-native.js" },
44 "src/manipulation.js",
45 { flag: "css", src: "src/css.js" },
47 { flag: "event-alias", src: "src/event-alias.js" },
48 { flag: "ajax", src: "src/ajax.js" },
49 { flag: "ajax/script", src: "src/ajax/script.js", needs: ["ajax"] },
50 { flag: "ajax/jsonp", src: "src/ajax/jsonp.js", needs: [ "ajax", "ajax/script" ] },
51 { flag: "ajax/xhr", src: "src/ajax/xhr.js", needs: ["ajax"] },
52 { flag: "effects", src: "src/effects.js", needs: ["css"] },
53 { flag: "offset", src: "src/offset.js", needs: ["css"] },
54 { flag: "dimensions", src: "src/dimensions.js", needs: ["css"] },
55 { flag: "deprecated", src: "src/deprecated.js" },
65 src: [ "dist/jquery.js" ],
67 jshintrc: "src/.jshintrc"
71 src: [ "Gruntfile.js" ],
77 // TODO: Once .jshintignore is supported, use that instead.
78 // issue located here: https://github.com/gruntjs/grunt-contrib-jshint/issues/1
79 src: [ "test/data/{test,testinit,testrunner}.js", "test/unit/**/*.js" ],
81 jshintrc: "test/.jshintrc"
87 tests: "ajax attributes callbacks core css data deferred dimensions effects event manipulation offset queue selector serialize support traversing Sizzle".split(" ")
91 files: [ "<%= jshint.grunt.src %>", "<%= jshint.tests.src %>", "src/**/*.js" ],
98 "dist/jquery.min.js": [ "dist/jquery.js" ]
101 banner: "/*! jQuery v<%= pkg.version %> | (c) 2005, 2013 jQuery Foundation, Inc. | jquery.org/license */",
102 sourceMap: "dist/jquery.min.map",
111 grunt.registerTask( "testswarm", function( commit, configFile ) {
113 testswarm = require( "testswarm" ),
115 pull = /PR-(\d+)/.exec( commit ),
116 config = grunt.file.readJSON( configFile ).jquery,
117 tests = grunt.config([ this.name, "tests" ]);
120 jobName = "jQuery pull <a href='https://github.com/jquery/jquery/pull/" +
121 pull[ 1 ] + "'>#" + pull[ 1 ] + "</a>";
123 jobName = "jQuery commit #<a href='https://github.com/jquery/jquery/commit/" +
124 commit + "'>" + commit.substr( 0, 10 ) + "</a>";
127 tests.forEach(function( test ) {
128 testUrls.push( config.testUrl + commit + "/test/index.html?module=" + test );
132 url: config.swarmUrl,
134 timeout: 1000 * 60 * 30,
137 authUsername: config.authUsername,
138 authToken: config.authToken,
140 runMax: config.runMax,
142 "runUrls[]": testUrls,
143 "browserSets[]": "popular-no-old-ie"
147 grunt.registerTask( "selector", "Build Sizzle-based selector module", function() {
149 var cfg = grunt.config("selector"),
152 api: grunt.file.read( cfg.apiFile ),
153 src: grunt.file.read( cfg.srcFile )
159 sizzle-jquery.js -> sizzle between "EXPOSE" blocks,
160 replace define & window.Sizzle assignment
164 if ( typeof define === "function" && define.amd ) {
165 define(function() { return Sizzle; });
167 window.Sizzle = Sizzle;
173 Sizzle.attr = jQuery.attr;
174 jQuery.find = Sizzle;
175 jQuery.expr = Sizzle.selectors;
176 jQuery.expr[":"] = jQuery.expr.pseudos;
177 jQuery.unique = Sizzle.uniqueSort;
178 jQuery.text = Sizzle.getText;
179 jQuery.isXMLDoc = Sizzle.isXML;
180 jQuery.contains = Sizzle.contains;
184 // Break into 3 pieces
185 parts = sizzle.src.split("// EXPOSE");
186 // Replace the if/else block with api
187 parts[1] = sizzle.api;
189 compiled = parts.join("");
191 grunt.verbose.writeln("Injected " + cfg.apiFile + " into " + cfg.srcFile);
193 // Write concatenated source to file, and ensure newline-only termination
194 grunt.file.write( name, compiled.replace( /\x0d\x0a/g, "\x0a" ) );
196 // Fail task if errors were logged.
197 if ( this.errorCount ) {
201 // Otherwise, print a success message.
202 grunt.log.writeln( "File '" + name + "' created." );
206 // Special "alias" task to make custom build creation less grawlix-y
207 grunt.registerTask( "custom", function() {
208 var done = this.async(),
209 args = [].slice.call(arguments),
210 modules = args.length ? args[0].replace(/,/g, ":") : "";
213 // Translation example
215 // grunt custom:+ajax,-dimensions,-effects,-offset
219 // grunt build:*:*:+ajax:-dimensions:-effects:-offset
221 grunt.log.writeln( "Creating custom build...\n" );
224 cmd: process.platform === "win32" ? "grunt.cmd" : "grunt",
225 args: [ "build:*:*:" + modules, "uglify", "dist" ]
226 }, function( err, result ) {
228 grunt.verbose.error();
233 grunt.log.writeln( result.stdout.replace("Done, without errors.", "") );
239 // Special concat/build task to handle various jQuery build requirements
241 grunt.registerMultiTask(
243 "Concatenate source (include/exclude modules with +/- flags), embed date/version",
246 // Concat specified files.
248 modules = this.flags,
249 optIn = !modules["*"],
250 explicit = optIn || Object.keys(modules).length > 1,
251 name = this.data.dest,
255 version = grunt.config( "pkg.version" ),
256 excluder = function( flag, needsFlag ) {
257 // optIn defaults implicit behavior to weak exclusion
258 if ( optIn && !modules[ flag ] && !modules[ "+" + flag ] ) {
259 excluded[ flag ] = false;
262 // explicit or inherited strong exclusion
263 if ( excluded[ needsFlag ] || modules[ "-" + flag ] ) {
264 excluded[ flag ] = true;
266 // explicit inclusion overrides weak exclusion
267 } else if ( excluded[ needsFlag ] === false &&
268 ( modules[ flag ] || modules[ "+" + flag ] ) ) {
270 delete excluded[ needsFlag ];
272 // ...all the way down
273 if ( deps[ needsFlag ] ) {
274 deps[ needsFlag ].forEach(function( subDep ) {
275 modules[ needsFlag ] = true;
276 excluder( needsFlag, subDep );
282 // append commit id to version
283 if ( process.env.COMMIT ) {
284 version += " " + process.env.COMMIT;
287 // figure out which files to exclude based on these rules in this order:
288 // dependency explicit exclude
289 // > explicit exclude
290 // > explicit include
291 // > dependency implicit exclude
292 // > implicit exclude
294 // * none (implicit exclude)
295 // *:* all (implicit include)
296 // *:*:-css all except css and dependents (explicit > implicit)
297 // *:*:-css:+effects same (excludes effects because explicit include is trumped by explicit exclude of dependency)
298 // *:+effects none except effects and its dependencies (explicit include trumps implicit exclude of dependency)
299 src.forEach(function( filepath ) {
300 var flag = filepath.flag;
306 // check for dependencies
307 if ( filepath.needs ) {
308 deps[ flag ] = filepath.needs;
309 filepath.needs.forEach(function( needsFlag ) {
310 excluder( flag, needsFlag );
316 // append excluded modules to version
317 if ( Object.keys( excluded ).length ) {
318 version += " -" + Object.keys( excluded ).join( ",-" );
319 // set pkg.version to version with excludes, so minified file picks it up
320 grunt.config.set( "pkg.version", version );
324 // conditionally concatenate source
325 src.forEach(function( filepath ) {
326 var flag = filepath.flag,
332 if ( excluded[ flag ] !== undefined ) {
334 ( "Excluding " + flag ).red,
335 ( "(" + filepath.src + ")" ).grey
338 omit = !filepath.alt;
340 flag += " alternate";
341 filepath.src = filepath.alt;
344 if ( excluded[ flag ] === undefined ) {
346 ( "Including " + flag ).green,
347 ( "(" + filepath.src + ")" ).grey
350 // If this module was actually specified by the
351 // builder, then set the flag to include it in the
353 if ( modules[ "+" + flag ] ) {
358 filepath = filepath.src;
360 // Only display the inclusion/exclusion list when handling
363 // Additionally, only display modules that have been specified
365 if ( explicit && specified ) {
366 messages.forEach(function( message ) {
367 grunt.log.writetableln( [ 27, 30 ], message );
373 compiled += grunt.file.read( filepath );
379 compiled = compiled.replace( /@VERSION/g, version )
380 .replace( "@DATE", function () {
381 var date = new Date();
391 // Write concatenated source to file
392 grunt.file.write( name, compiled );
394 // Fail task if errors were logged.
395 if ( this.errorCount ) {
399 // Otherwise, print a success message.
400 grunt.log.writeln( "File '" + name + "' created." );
403 // Process files for distribution
404 grunt.registerTask( "dist", function() {
405 var flags, paths, stored;
407 // Check for stored destination paths
408 // ( set in dist/.destination.json )
409 stored = Object.keys( grunt.config("dst") );
411 // Allow command line input as well
412 flags = Object.keys( this.flags );
414 // Combine all output target paths
415 paths = [].concat( stored, flags ).filter(function( path ) {
419 // Ensure the dist files are pure ASCII
420 var fs = require("fs"),
423 distpaths.forEach(function( filename ) {
425 text = fs.readFileSync( filename, "utf8" );
427 // Ensure files use only \n for line endings, not \r\n
428 if ( /\x0d\x0a/.test( text ) ) {
429 grunt.log.writeln( filename + ": Incorrect line endings (\\r\\n)" );
433 // Ensure only ASCII chars so script tags don't need a charset attribute
434 if ( text.length !== Buffer.byteLength( text, "utf8" ) ) {
435 grunt.log.writeln( filename + ": Non-ASCII characters detected:" );
436 for ( i = 0; i < text.length; i++ ) {
437 c = text.charCodeAt( i );
439 grunt.log.writeln( "- position " + i + ": " + c );
440 grunt.log.writeln( "-- " + text.substring( i - 20, i + 20 ) );
447 // Modify map/min so that it points to files in the same folder;
448 // see https://github.com/mishoo/UglifyJS2/issues/47
449 if ( /\.map$/.test( filename ) ) {
450 text = text.replace( /"dist\//g, "\"" );
451 fs.writeFileSync( filename, text, "utf-8" );
452 } else if ( /\.min\.js$/.test( filename ) ) {
453 // Wrap sourceMap directive in multiline comments (#13274)
454 text = text.replace( /\n?(\/\/@\s*sourceMappingURL=)(.*)/,
455 function( _, directive, path ) {
456 map = "\n" + directive + path.replace( /^dist\//, "" );
460 text = text.replace( /(^\/\*[\w\W]*?)\s*\*\/|$/,
461 function( _, comment ) {
462 return ( comment || "\n/*" ) + map + "\n*/";
465 fs.writeFileSync( filename, text, "utf-8" );
468 // Optionally copy dist files to other locations
469 paths.forEach(function( path ) {
472 if ( !/\/$/.test( path ) ) {
476 created = path + filename.replace( "dist/", "" );
477 grunt.file.write( created, text );
478 grunt.log.writeln( "File '" + created + "' created." );
485 // Load grunt tasks from NPM packages
486 grunt.loadNpmTasks("grunt-compare-size");
487 grunt.loadNpmTasks("grunt-git-authors");
488 grunt.loadNpmTasks("grunt-update-submodules");
489 grunt.loadNpmTasks("grunt-contrib-watch");
490 grunt.loadNpmTasks("grunt-contrib-jshint");
491 grunt.loadNpmTasks("grunt-contrib-uglify");
494 grunt.registerTask( "default", [ "update_submodules", "selector", "build:*:*", "jshint", "uglify", "dist:*" ] );
496 // Short list as a high frequency watch task
497 grunt.registerTask( "dev", [ "selector", "build:*:*", "jshint" ] );