]> source.dussan.org Git - jquery.git/commitdiff
Move jQuery specific tasks to independent files. Close gh-1334.
authorOleg Gaidarenko <markelog@gmail.com>
Fri, 16 Aug 2013 13:48:00 +0000 (09:48 -0400)
committerTimmy Willison <timmywillisn@gmail.com>
Fri, 16 Aug 2013 14:04:31 +0000 (10:04 -0400)
Also:
* Confirm build task to the style guide
* Use grunt API to load jQuery specific tasks
* Add "use strict"; statements

Conflicts:
Gruntfile.js
build/tasks/build.js

Gruntfile.js
build/build.js [deleted file]
build/tasks/build.js [new file with mode: 0644]
build/tasks/dist.js [new file with mode: 0644]
build/tasks/testswarm.js [new file with mode: 0644]
build/tasks/uglify.js [new file with mode: 0644]

index 538f6fb80572f0e1b59bf44c9f3e84a83fa39b97..5cad93f65a5eabee45b53b0007178862bbae0268 100644 (file)
@@ -2,15 +2,7 @@ module.exports = function( grunt ) {
 
        "use strict";
 
-       // Integrate build task
-       require( "./build/build" )( grunt );
-
-       var distpaths = [
-                       "dist/jquery.js",
-                       "dist/jquery.min.map",
-                       "dist/jquery.min.js"
-               ],
-               gzip = require("gzip-js"),
+       var gzip = require( "gzip-js" ),
                readOptionalJSON = function( filepath ) {
                        var data = {};
                        try {
@@ -18,7 +10,6 @@ module.exports = function( grunt ) {
                        } catch(e) {}
                        return data;
                },
-               fs = require( "fs" ),
                srcHintOptions = readOptionalJSON( "src/.jshintrc" );
 
        // The concatenated file won't pass onevar
@@ -66,7 +57,7 @@ module.exports = function( grunt ) {
                                options: srcHintOptions
                        },
                        grunt: {
-                               src: [ "Gruntfile.js", "build/build.js" ],
+                               src: [ "Gruntfile.js", "build/tasks/*" ],
                                options: {
                                        jshintrc: ".jshintrc"
                                }
@@ -136,186 +127,16 @@ module.exports = function( grunt ) {
                }
        });
 
-       grunt.registerTask( "testswarm", function( commit, configFile ) {
-               var jobName,
-                       testswarm = require( "testswarm" ),
-                       runs = {},
-                       done = this.async(),
-                       pull = /PR-(\d+)/.exec( commit ),
-                       config = grunt.file.readJSON( configFile ).jquery,
-                       tests = grunt.config([ this.name, "tests" ]);
-
-               if ( pull ) {
-                       jobName = "jQuery pull <a href='https://github.com/jquery/jquery/pull/" +
-                               pull[ 1 ] + "'>#" + pull[ 1 ] + "</a>";
-               } else {
-                       jobName = "jQuery commit #<a href='https://github.com/jquery/jquery/commit/" +
-                               commit + "'>" + commit.substr( 0, 10 ) + "</a>";
-               }
-
-               tests.forEach(function( test ) {
-                       runs[test] = config.testUrl + commit + "/test/index.html?module=" + test;
-               });
-
-               testswarm.createClient( {
-                       url: config.swarmUrl,
-                       pollInterval: 10000,
-                       timeout: 1000 * 60 * 30
-               } )
-               .addReporter( testswarm.reporters.cli )
-               .auth( {
-                       id: config.authUsername,
-                       token: config.authToken
-               })
-               .addjob(
-                       {
-                               name: jobName,
-                               runs: runs,
-                               runMax: config.runMax,
-                               browserSets: config.browserSets
-                       }, function( err, passed ) {
-                               if ( err ) {
-                                       grunt.log.error( err );
-                               }
-                               done( passed );
-                       }
-               );
-       });
-
-       // Process files for distribution
-       grunt.registerTask( "dist", function() {
-               var stored, flags, paths, nonascii;
-
-               // Check for stored destination paths
-               // ( set in dist/.destination.json )
-               stored = Object.keys( grunt.config( "dst" ) );
-
-               // Allow command line input as well
-               flags = Object.keys( this.flags );
-
-               // Combine all output target paths
-               paths = [].concat( stored, flags ).filter(function( path ) {
-                       return path !== "*";
-               });
-
-               // Ensure the dist files are pure ASCII
-               nonascii = false;
-
-               distpaths.forEach(function( filename ) {
-                       var i, c,
-                               text = fs.readFileSync( filename, "utf8" );
-
-                       // Ensure files use only \n for line endings, not \r\n
-                       if ( /\x0d\x0a/.test( text ) ) {
-                               grunt.log.writeln( filename + ": Incorrect line endings (\\r\\n)" );
-                               nonascii = true;
-                       }
-
-                       // Ensure only ASCII chars so script tags don't need a charset attribute
-                       if ( text.length !== Buffer.byteLength( text, "utf8" ) ) {
-                               grunt.log.writeln( filename + ": Non-ASCII characters detected:" );
-                               for ( i = 0; i < text.length; i++ ) {
-                                       c = text.charCodeAt( i );
-                                       if ( c > 127 ) {
-                                               grunt.log.writeln( "- position " + i + ": " + c );
-                                               grunt.log.writeln( "-- " + text.substring( i - 20, i + 20 ) );
-                                               break;
-                                       }
-                               }
-                               nonascii = true;
-                       }
-
-                       // Modify map/min so that it points to files in the same folder;
-                       // see https://github.com/mishoo/UglifyJS2/issues/47
-                       if ( /\.map$/.test( filename ) ) {
-                               text = text.replace( /"dist\//g, "\"" );
-                               fs.writeFileSync( filename, text, "utf-8" );
-
-                       // Use our hard-coded sourceMap directive instead of the autogenerated one (#13274; #13776)
-                       } else if ( /\.min\.js$/.test( filename ) ) {
-                               i = 0;
-                               text = text.replace( /(?:\/\*|)\n?\/\/@\s*sourceMappingURL=.*(\n\*\/|)/g,
-                                       function( match ) {
-                                               if ( i++ ) {
-                                                       return "";
-                                               }
-                                               return match;
-                                       });
-                               fs.writeFileSync( filename, text, "utf-8" );
-                       }
-
-                       // Optionally copy dist files to other locations
-                       paths.forEach(function( path ) {
-                               var created;
-
-                               if ( !/\/$/.test( path ) ) {
-                                       path += "/";
-                               }
-
-                               created = path + filename.replace( "dist/", "" );
-                               grunt.file.write( created, text );
-                               grunt.log.writeln( "File '" + created + "' created." );
-                       });
-               });
-
-               return !nonascii;
-       });
-
-       // Work around grunt-contrib-uglify sourceMap issues (jQuery #13776)
-       grunt.registerMultiTask( "pre-uglify", function() {
-               var banner = this.options().banner;
-
-               this.files.forEach(function( mapping ) {
-                       // Join src
-                       var input = mapping.src.map(function( file ) {
-                               var contents = grunt.file.read( file );
-
-                               // Strip banners
-                               return contents
-                                       // Remove the main jQuery banner, it'll be replaced by the new banner anyway.
-                                       .replace( /^\/\*![\W\w]*?\*\/\n?/g, "" )
-                                       // Strip other banners preserving line count.
-                                       .replace( /^\/\*!(?:.|\n)*?\*\/\n?/gm, function ( match ) {
-                                               return match.replace( /[^\n]/gm, "" );
-                                       });
-                       }).join("\n");
-
-                       // Write temp file (with optional banner)
-                       grunt.file.write( mapping.dest, ( banner || "" ) + input );
-               });
-       });
-
-       // Change the map file to point back to jquery.js instead of jquery.pre-min.js.
-       // The problem is caused by the pre-uglify task.
-       // Also, remove temporary files.
-       grunt.registerMultiTask( "post-uglify", function() {
-               this.files.forEach(function( mapping ) {
-                       var mapFileName = mapping.src[ 0 ];
-
-                       // Rename the file to a temporary name.
-                       fs.renameSync( mapFileName, mapping.dest);
-                       grunt.file.write( mapFileName, grunt.file.read( mapping.dest )
-                               // The uglify task erroneously prepends dist/ to file names.
-                               .replace( /"dist\//g, "\"" )
-                               // Refer to the source jquery.js, not the temporary jquery.pre-min.js.
-                               .replace( /\.pre-min\./g, "." )
-                               // There's already a pragma at the beginning of the file, remove the one at the end.
-                               .replace( /\/\/@ sourceMappingURL=jquery\.min\.map$/g, "" ));
-               });
-
-               // Remove temporary files.
-               this.options().tempFiles.forEach(function( fileName ) {
-                       fs.unlink( fileName );
-               });
-       });
-
        // Load grunt tasks from NPM packages
-       grunt.loadNpmTasks("grunt-compare-size");
-       grunt.loadNpmTasks("grunt-git-authors");
-       grunt.loadNpmTasks("grunt-contrib-watch");
-       grunt.loadNpmTasks("grunt-contrib-jshint");
-       grunt.loadNpmTasks("grunt-contrib-uglify");
-       grunt.loadNpmTasks("grunt-jsonlint");
+       grunt.loadNpmTasks( "grunt-compare-size" );
+       grunt.loadNpmTasks( "grunt-git-authors" );
+       grunt.loadNpmTasks( "grunt-contrib-watch" );
+       grunt.loadNpmTasks( "grunt-contrib-jshint" );
+       grunt.loadNpmTasks( "grunt-contrib-uglify" );
+       grunt.loadNpmTasks( "grunt-jsonlint" );
+
+       // Integrate jQuery specific tasks
+       grunt.loadTasks( "build/tasks" );
 
        // Short list as a high frequency watch task
        grunt.registerTask( "dev", [ "build:*:*", "jshint" ] );
diff --git a/build/build.js b/build/build.js
deleted file mode 100644 (file)
index eb68f68..0000000
+++ /dev/null
@@ -1,226 +0,0 @@
-/**
- * Special concat/build task to handle various jQuery build requirements
- * Concats AMD modules, removes their definitions, and includes/excludes specified modules
- */
-
-module.exports = function( grunt ) {
-
-       var fs = require("fs"),
-               srcFolder = __dirname + "/../src/",
-               rdefineEnd = /\}\);[^}\w]*$/,
-               // This is temporary until the skipSemiColonInsertion option makes it to NPM
-               requirejs = require("./r"),
-               config = {
-                       baseUrl: "src",
-                       name: "jquery",
-                       out: "dist/jquery.js",
-                       // We have multiple minify steps
-                       optimize: "none",
-                       skipSemiColonInsertion: true,
-                       wrap: {
-                               startFile: "src/intro.js",
-                               endFile: "src/outro.js"
-                       },
-                       onBuildWrite: convert
-               };
-
-       /**
-        * Strip all definitions generated by requirejs
-        * Convert "var" modules to var declarations
-        * "var module" means the module only contains a return statement that should be converted to a var declaration
-        * This is indicated by including the file in any "var" folder
-        * @param {String} name
-        * @param {String} path
-        * @param {String} contents The contents to be written (including their AMD wrappers)
-        */
-       function convert( name, path, contents ) {
-               // Convert var modules
-               if ( /.\/var\//.test( path ) ) {
-                       contents = contents
-                               .replace( /define\([\w\W]*?return/, "var " + (/var\/([\w-]+)/.exec(name)[1]) + " =" )
-                               .replace( rdefineEnd, "" );
-
-               // Sizzle treatment
-               } else if ( /\/sizzle$/.test( name ) ) {
-                       contents = "var Sizzle =\n" + contents
-                               // Remove EXPOSE lines from Sizzle
-                               .replace( /\/\/\s*EXPOSE[\w\W]*\/\/\s*EXPOSE/, "return Sizzle;" );
-
-               } else {
-
-                       // Ignore jQuery's return statement (the only necessary one)
-                       if ( name !== "jquery" ) {
-                               contents = contents
-                                       .replace( /return\s+[^\}]+(\}\);[^\w\}]*)$/, "$1" );
-                       }
-
-                       // Remove define wrappers, closure ends, and empty declarations
-                       contents = contents
-                               .replace( /define\([^{]*?{/, "" )
-                               .replace( rdefineEnd, "" )
-                               // Remove empty definitions
-                               .replace( /define\(\[[^\]]+\]\)[\W\n]+$/, "" );
-               }
-               return contents;
-       }
-
-       grunt.registerMultiTask(
-               "build",
-               "Concatenate source, remove sub AMD definitions, (include/exclude modules with +/- flags), embed date/version",
-       function() {
-               var flag,
-                       done = this.async(),
-                       flags = this.flags,
-                       name = this.data.dest,
-                       minimum = this.data.minimum,
-                       removeWith = this.data.removeWith,
-                       excluded = [],
-                       included = [],
-                       version = grunt.config( "pkg.version" ),
-                       /**
-                        * Recursively calls the excluder to remove on all modules in the list
-                        * @param {Array} list
-                        * @param {String} [prepend] Prepend this to the module name. Indicates we're walking a directory
-                        */
-                       excludeList = function( list, prepend ) {
-                               if ( list ) {
-                                       prepend = prepend ? prepend + "/" : "";
-                                       list.forEach(function( module ) {
-                                               // Exclude var modules as well
-                                               if ( module === "var" ) {
-                                                       excludeList( fs.readdirSync( srcFolder + prepend + module ), prepend + module );
-                                                       return;
-                                               }
-                                               if ( prepend ) {
-                                                       // Skip if this is not a js file and we're walking files in a dir
-                                                       if ( !(module = /([\w-\/]+)\.js$/.exec( module )) ) {
-                                                               return;
-                                                       }
-                                                       // Prepend folder name if passed
-                                                       // Remove .js extension
-                                                       module = prepend + module[1];
-                                               }
-
-                                               // Avoid infinite recursion
-                                               if ( excluded.indexOf( module ) === -1 ) {
-                                                       excluder( "-" + module );
-                                               }
-                                       });
-                               }
-                       },
-                       /**
-                        * Adds the specified module to the excluded or included list, depending on the flag
-                        * @param {String} flag A module path relative to the src directory starting with + or - to indicate whether it should included or excluded
-                        */
-                       excluder = function( flag ) {
-                               var m = /^(\+|\-|)([\w\/-]+)$/.exec( flag ),
-                                       exclude = m[1] === "-",
-                                       module = m[2];
-
-                               if ( exclude ) {
-                                       // Can't exclude certain modules
-                                       if ( minimum.indexOf( module ) === -1 ) {
-                                               // Add to excluded
-                                               if ( excluded.indexOf( module ) === -1 ) {
-                                                       grunt.log.writeln( flag );
-                                                       excluded.push( module );
-                                                       // Exclude all files in the folder of the same name
-                                                       // These are the removable dependencies
-                                                       // It's fine if the directory is not there
-                                                       try {
-                                                               excludeList( fs.readdirSync( srcFolder + module ), module );
-                                                       } catch( e ) {
-                                                               grunt.verbose.writeln( e );
-                                                       }
-                                               }
-                                               // Check removeWith list
-                                               excludeList( removeWith[ module ] );
-                                       } else {
-                                               grunt.log.error( "Module \"" + module + "\" is a mimimum requirement.");
-                                       }
-                               } else {
-                                       grunt.log.writeln( flag );
-                                       included.push( module );
-                               }
-                       };
-
-               // append commit id to version
-               if ( process.env.COMMIT ) {
-                       version += " " + process.env.COMMIT;
-               }
-
-               // figure out which files to exclude based on these rules in this order:
-               //  dependency explicit exclude
-               //  > explicit exclude
-               //  > explicit include
-               //  > dependency implicit exclude
-               //  > implicit exclude
-               // examples:
-               //  *                  none (implicit exclude)
-               //  *:*                all (implicit include)
-               //  *:*:-css           all except css and dependents (explicit > implicit)
-               //  *:*:-css:+effects  same (excludes effects because explicit include is trumped by explicit exclude of dependency)
-               //  *:+effects         none except effects and its dependencies (explicit include trumps implicit exclude of dependency)
-               for ( flag in flags ) {
-                       if ( flag !== "*" ) {
-                               excluder( flag );
-                       }
-               }
-
-               grunt.verbose.writeflags( excluded, "Excluded" );
-               grunt.verbose.writeflags( included, "Included" );
-
-               // append excluded modules to version
-               if ( excluded.length ) {
-                       version += " -" + excluded.join( ",-" );
-                       // set pkg.version to version with excludes, so minified file picks it up
-                       grunt.config.set( "pkg.version", version );
-                       grunt.verbose.writeln( "Version changed to " + version );
-                       // Have to use shallow or core will get excluded since it is a dependency
-                       config.excludeShallow = excluded;
-               }
-               config.include = included;
-
-               /**
-                * Handle Final output from the optimizer
-                * @param {String} compiled
-                */
-               config.out = function( compiled ) {
-                       compiled = compiled
-                               // Embed Version
-                               .replace( /@VERSION/g, version )
-                               // Embed Date
-                               // yyyy-mm-ddThh:mmZ
-                               .replace( /@DATE/g, ( new Date() ).toISOString().replace( /:\d+\.\d+Z$/, "Z" ) );
-
-                       // Write concatenated source to file
-                       grunt.file.write( name, compiled );
-               };
-
-               // Trace dependencies and concatenate files
-               requirejs.optimize( config, function( response ) {
-                       grunt.verbose.writeln( response );
-                       grunt.log.ok( "File '" + name + "' created." );
-                       done();
-               }, function( err ) {
-                       done( err );
-               });
-       });
-
-       // Special "alias" task to make custom build creation less grawlix-y
-       // Translation example
-       //
-       //   grunt custom:+ajax,-dimensions,-effects,-offset
-       //
-       // Becomes:
-       //
-       //   grunt build:*:*:+ajax:-dimensions:-effects:-offset
-       grunt.registerTask( "custom", function() {
-               var args = [].slice.call(arguments),
-                       modules = args.length ? args[0].replace(/,/g, ":") : "";
-
-               grunt.log.writeln( "Creating custom build...\n" );
-
-               grunt.task.run([ "build:*:*:" + modules, "pre-uglify", "uglify", "dist" ]);
-       });
-};
diff --git a/build/tasks/build.js b/build/tasks/build.js
new file mode 100644 (file)
index 0000000..ab580fd
--- /dev/null
@@ -0,0 +1,228 @@
+/**
+ * Special concat/build task to handle various jQuery build requirements
+ * Concats AMD modules, removes their definitions, and includes/excludes specified modules
+ */
+
+module.exports = function( grunt ) {
+
+       "use strict";
+
+       var fs = require( "fs" ),
+               srcFolder = __dirname + "/../src/",
+               rdefineEnd = /\}\);[^}\w]*$/,
+               // This is temporary until the skipSemiColonInsertion option makes it to NPM
+               requirejs = require( "../r" ),
+               config = {
+                       baseUrl: "src",
+                       name: "jquery",
+                       out: "dist/jquery.js",
+                       // We have multiple minify steps
+                       optimize: "none",
+                       skipSemiColonInsertion: true,
+                       wrap: {
+                               startFile: "src/intro.js",
+                               endFile: "src/outro.js"
+                       },
+                       onBuildWrite: convert
+               };
+
+       /**
+        * Strip all definitions generated by requirejs
+        * Convert "var" modules to var declarations
+        * "var module" means the module only contains a return statement that should be converted to a var declaration
+        * This is indicated by including the file in any "var" folder
+        * @param {String} name
+        * @param {String} path
+        * @param {String} contents The contents to be written (including their AMD wrappers)
+        */
+       function convert( name, path, contents ) {
+               // Convert var modules
+               if ( /.\/var\//.test( path ) ) {
+                       contents = contents
+                               .replace( /define\([\w\W]*?return/, "var " + (/var\/([\w-]+)/.exec(name)[1]) + " =" )
+                               .replace( rdefineEnd, "" );
+
+               // Sizzle treatment
+               } else if ( /\/sizzle$/.test( name ) ) {
+                       contents = "var Sizzle =\n" + contents
+                               // Remove EXPOSE lines from Sizzle
+                               .replace( /\/\/\s*EXPOSE[\w\W]*\/\/\s*EXPOSE/, "return Sizzle;" );
+
+               } else {
+
+                       // Ignore jQuery's return statement (the only necessary one)
+                       if ( name !== "jquery" ) {
+                               contents = contents
+                                       .replace( /return\s+[^\}]+(\}\);[^\w\}]*)$/, "$1" );
+                       }
+
+                       // Remove define wrappers, closure ends, and empty declarations
+                       contents = contents
+                               .replace( /define\([^{]*?{/, "" )
+                               .replace( rdefineEnd, "" )
+                               // Remove empty definitions
+                               .replace( /define\(\[[^\]]+\]\)[\W\n]+$/, "" );
+               }
+               return contents;
+       }
+
+       grunt.registerMultiTask(
+               "build",
+               "Concatenate source, remove sub AMD definitions, (include/exclude modules with +/- flags), embed date/version",
+       function() {
+               var flag,
+                       done = this.async(),
+                       flags = this.flags,
+                       name = this.data.dest,
+                       minimum = this.data.minimum,
+                       removeWith = this.data.removeWith,
+                       excluded = [],
+                       included = [],
+                       version = grunt.config( "pkg.version" ),
+                       /**
+                        * Recursively calls the excluder to remove on all modules in the list
+                        * @param {Array} list
+                        * @param {String} [prepend] Prepend this to the module name. Indicates we're walking a directory
+                        */
+                       excludeList = function( list, prepend ) {
+                               if ( list ) {
+                                       prepend = prepend ? prepend + "/" : "";
+                                       list.forEach(function( module ) {
+                                               // Exclude var modules as well
+                                               if ( module === "var" ) {
+                                                       excludeList( fs.readdirSync( srcFolder + prepend + module ), prepend + module );
+                                                       return;
+                                               }
+                                               if ( prepend ) {
+                                                       // Skip if this is not a js file and we're walking files in a dir
+                                                       if ( !(module = /([\w-\/]+)\.js$/.exec( module )) ) {
+                                                               return;
+                                                       }
+                                                       // Prepend folder name if passed
+                                                       // Remove .js extension
+                                                       module = prepend + module[1];
+                                               }
+
+                                               // Avoid infinite recursion
+                                               if ( excluded.indexOf( module ) === -1 ) {
+                                                       excluder( "-" + module );
+                                               }
+                                       });
+                               }
+                       },
+                       /**
+                        * Adds the specified module to the excluded or included list, depending on the flag
+                        * @param {String} flag A module path relative to the src directory starting with + or - to indicate whether it should included or excluded
+                        */
+                       excluder = function( flag ) {
+                               var m = /^(\+|\-|)([\w\/-]+)$/.exec( flag ),
+                                       exclude = m[ 1 ] === "-",
+                                       module = m[ 2 ];
+
+                               if ( exclude ) {
+                                       // Can't exclude certain modules
+                                       if ( minimum.indexOf( module ) === -1 ) {
+                                               // Add to excluded
+                                               if ( excluded.indexOf( module ) === -1 ) {
+                                                       grunt.log.writeln( flag );
+                                                       excluded.push( module );
+                                                       // Exclude all files in the folder of the same name
+                                                       // These are the removable dependencies
+                                                       // It's fine if the directory is not there
+                                                       try {
+                                                               excludeList( fs.readdirSync( srcFolder + module ), module );
+                                                       } catch( e ) {
+                                                               grunt.verbose.writeln( e );
+                                                       }
+                                               }
+                                               // Check removeWith list
+                                               excludeList( removeWith[ module ] );
+                                       } else {
+                                               grunt.log.error( "Module \"" + module + "\" is a mimimum requirement.");
+                                       }
+                               } else {
+                                       grunt.log.writeln( flag );
+                                       included.push( module );
+                               }
+                       };
+
+               // append commit id to version
+               if ( process.env.COMMIT ) {
+                       version += " " + process.env.COMMIT;
+               }
+
+               // figure out which files to exclude based on these rules in this order:
+               //  dependency explicit exclude
+               //  > explicit exclude
+               //  > explicit include
+               //  > dependency implicit exclude
+               //  > implicit exclude
+               // examples:
+               //  *                  none (implicit exclude)
+               //  *:*                all (implicit include)
+               //  *:*:-css           all except css and dependents (explicit > implicit)
+               //  *:*:-css:+effects  same (excludes effects because explicit include is trumped by explicit exclude of dependency)
+               //  *:+effects         none except effects and its dependencies (explicit include trumps implicit exclude of dependency)
+               for ( flag in flags ) {
+                       if ( flag !== "*" ) {
+                               excluder( flag );
+                       }
+               }
+
+               grunt.verbose.writeflags( excluded, "Excluded" );
+               grunt.verbose.writeflags( included, "Included" );
+
+               // append excluded modules to version
+               if ( excluded.length ) {
+                       version += " -" + excluded.join( ",-" );
+                       // set pkg.version to version with excludes, so minified file picks it up
+                       grunt.config.set( "pkg.version", version );
+                       grunt.verbose.writeln( "Version changed to " + version );
+                       // Have to use shallow or core will get excluded since it is a dependency
+                       config.excludeShallow = excluded;
+               }
+               config.include = included;
+
+               /**
+                * Handle Final output from the optimizer
+                * @param {String} compiled
+                */
+               config.out = function( compiled ) {
+                       compiled = compiled
+                               // Embed Version
+                               .replace( /@VERSION/g, version )
+                               // Embed Date
+                               // yyyy-mm-ddThh:mmZ
+                               .replace( /@DATE/g, ( new Date() ).toISOString().replace( /:\d+\.\d+Z$/, "Z" ) );
+
+                       // Write concatenated source to file
+                       grunt.file.write( name, compiled );
+               };
+
+               // Trace dependencies and concatenate files
+               requirejs.optimize( config, function( response ) {
+                       grunt.verbose.writeln( response );
+                       grunt.log.ok( "File '" + name + "' created." );
+                       done();
+               }, function( err ) {
+                       done( err );
+               });
+       });
+
+       // Special "alias" task to make custom build creation less grawlix-y
+       // Translation example
+       //
+       //   grunt custom:+ajax,-dimensions,-effects,-offset
+       //
+       // Becomes:
+       //
+       //   grunt build:*:*:+ajax:-dimensions:-effects:-offset
+       grunt.registerTask( "custom", function() {
+               var args = [].slice.call( arguments ),
+                       modules = args.length ? args[ 0 ].replace( /,/g, ":" ) : "";
+
+               grunt.log.writeln( "Creating custom build...\n" );
+
+               grunt.task.run([ "build:*:*:" + modules, "pre-uglify", "uglify", "dist" ]);
+       });
+};
diff --git a/build/tasks/dist.js b/build/tasks/dist.js
new file mode 100644 (file)
index 0000000..591078c
--- /dev/null
@@ -0,0 +1,90 @@
+module.exports = function( grunt ) {
+
+       "use strict";
+
+       var fs = require( "fs" ),
+               distpaths = [
+                       "dist/jquery.js",
+                       "dist/jquery.min.map",
+                       "dist/jquery.min.js"
+               ];
+
+       // Process files for distribution
+       grunt.registerTask( "dist", function() {
+               var stored, flags, paths, nonascii;
+
+               // Check for stored destination paths
+               // ( set in dist/.destination.json )
+               stored = Object.keys( grunt.config( "dst" ) );
+
+               // Allow command line input as well
+               flags = Object.keys( this.flags );
+
+               // Combine all output target paths
+               paths = [].concat( stored, flags ).filter(function( path ) {
+                       return path !== "*";
+               });
+
+               // Ensure the dist files are pure ASCII
+               nonascii = false;
+
+               distpaths.forEach(function( filename ) {
+                       var i, c,
+                               text = fs.readFileSync( filename, "utf8" );
+
+                       // Ensure files use only \n for line endings, not \r\n
+                       if ( /\x0d\x0a/.test( text ) ) {
+                               grunt.log.writeln( filename + ": Incorrect line endings (\\r\\n)" );
+                               nonascii = true;
+                       }
+
+                       // Ensure only ASCII chars so script tags don't need a charset attribute
+                       if ( text.length !== Buffer.byteLength( text, "utf8" ) ) {
+                               grunt.log.writeln( filename + ": Non-ASCII characters detected:" );
+                               for ( i = 0; i < text.length; i++ ) {
+                                       c = text.charCodeAt( i );
+                                       if ( c > 127 ) {
+                                               grunt.log.writeln( "- position " + i + ": " + c );
+                                               grunt.log.writeln( "-- " + text.substring( i - 20, i + 20 ) );
+                                               break;
+                                       }
+                               }
+                               nonascii = true;
+                       }
+
+                       // Modify map/min so that it points to files in the same folder;
+                       // see https://github.com/mishoo/UglifyJS2/issues/47
+                       if ( /\.map$/.test( filename ) ) {
+                               text = text.replace( /"dist\//g, "\"" );
+                               fs.writeFileSync( filename, text, "utf-8" );
+
+                       // Use our hard-coded sourceMap directive instead of the autogenerated one (#13274; #13776)
+                       } else if ( /\.min\.js$/.test( filename ) ) {
+                               i = 0;
+                               text = text.replace( /(?:\/\*|)\n?\/\/@\s*sourceMappingURL=.*(\n\*\/|)/g,
+                                       function( match ) {
+                                               if ( i++ ) {
+                                                       return "";
+                                               }
+                                               return match;
+                                       });
+                               fs.writeFileSync( filename, text, "utf-8" );
+                       }
+
+                       // Optionally copy dist files to other locations
+                       paths.forEach(function( path ) {
+                               var created;
+
+                               if ( !/\/$/.test( path ) ) {
+                                       path += "/";
+                               }
+
+                               created = path + filename.replace( "dist/", "" );
+                               grunt.file.write( created, text );
+                               grunt.log.writeln( "File '" + created + "' created." );
+                       });
+               });
+
+               return !nonascii;
+       });
+};
diff --git a/build/tasks/testswarm.js b/build/tasks/testswarm.js
new file mode 100644 (file)
index 0000000..92b696e
--- /dev/null
@@ -0,0 +1,50 @@
+module.exports = function( grunt ) {
+
+       "use strict";
+
+       grunt.registerTask( "testswarm", function( commit, configFile ) {
+               var jobName,
+                       testswarm = require( "testswarm" ),
+                       runs = {},
+                       done = this.async(),
+                       pull = /PR-(\d+)/.exec( commit ),
+                       config = grunt.file.readJSON( configFile ).jquery,
+                       tests = grunt.config([ this.name, "tests" ]);
+
+               if ( pull ) {
+                       jobName = "jQuery pull <a href='https://github.com/jquery/jquery/pull/" +
+                               pull[ 1 ] + "'>#" + pull[ 1 ] + "</a>";
+               } else {
+                       jobName = "jQuery commit #<a href='https://github.com/jquery/jquery/commit/" +
+                               commit + "'>" + commit.substr( 0, 10 ) + "</a>";
+               }
+
+               tests.forEach(function( test ) {
+                       runs[ test ] = config.testUrl + commit + "/test/index.html?module=" + test;
+               });
+
+               testswarm.createClient( {
+                       url: config.swarmUrl,
+                       pollInterval: 10000,
+                       timeout: 1000 * 60 * 30
+               } )
+               .addReporter( testswarm.reporters.cli )
+               .auth( {
+                       id: config.authUsername,
+                       token: config.authToken
+               })
+               .addjob(
+                       {
+                               name: jobName,
+                               runs: runs,
+                               runMax: config.runMax,
+                               browserSets: config.browserSets
+                       }, function( err, passed ) {
+                               if ( err ) {
+                                       grunt.log.error( err );
+                               }
+                               done( passed );
+                       }
+               );
+       });
+};
diff --git a/build/tasks/uglify.js b/build/tasks/uglify.js
new file mode 100644 (file)
index 0000000..7c1a01b
--- /dev/null
@@ -0,0 +1,54 @@
+module.exports = function( grunt ) {
+
+       "use strict";
+
+       var fs = require( "fs" );
+
+       // Work around grunt-contrib-uglify sourceMap issues (jQuery #13776)
+       grunt.registerMultiTask( "pre-uglify", function() {
+               var banner = this.options().banner;
+
+               this.files.forEach(function( mapping ) {
+                       // Join src
+                       var input = mapping.src.map(function( file ) {
+                               var contents = grunt.file.read( file );
+
+                               // Strip banners
+                               return contents
+                                       // Remove the main jQuery banner, it'll be replaced by the new banner anyway.
+                                       .replace( /^\/\*![\W\w]*?\*\/\n?/g, "" )
+                                       // Strip other banners preserving line count.
+                                       .replace( /^\/\*!(?:.|\n)*?\*\/\n?/gm, function ( match ) {
+                                               return match.replace( /[^\n]/gm, "" );
+                                       });
+                       }).join("\n");
+
+                       // Write temp file (with optional banner)
+                       grunt.file.write( mapping.dest, ( banner || "" ) + input );
+               });
+       });
+
+       // Change the map file to point back to jquery.js instead of jquery.pre-min.js.
+       // The problem is caused by the pre-uglify task.
+       // Also, remove temporary files.
+       grunt.registerMultiTask( "post-uglify", function() {
+               this.files.forEach(function( mapping ) {
+                       var mapFileName = mapping.src[ 0 ];
+
+                       // Rename the file to a temporary name.
+                       fs.renameSync( mapFileName, mapping.dest);
+                       grunt.file.write( mapFileName, grunt.file.read( mapping.dest )
+                               // The uglify task erroneously prepends dist/ to file names.
+                               .replace( /"dist\//g, "\"" )
+                               // Refer to the source jquery.js, not the temporary jquery.pre-min.js.
+                               .replace( /\.pre-min\./g, "." )
+                               // There's already a pragma at the beginning of the file, remove the one at the end.
+                               .replace( /\/\/@ sourceMappingURL=jquery\.min\.map$/g, "" ));
+               });
+
+               // Remove temporary files.
+               this.options().tempFiles.forEach(function( fileName ) {
+                       fs.unlink( fileName );
+               });
+       });
+};