123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599 |
- module.exports = function( grunt ) {
- "use strict";
- var distpaths = [
- "dist/jquery.js",
- "dist/jquery.min.map",
- "dist/jquery.min.js"
- ],
- gzip = require("gzip-js"),
- readOptionalJSON = function( filepath ) {
- var data = {};
- try {
- data = grunt.file.readJSON( filepath );
- } catch(e) {}
- return data;
- },
- srcHintOptions = readOptionalJSON("src/.jshintrc");
- // The concatenated file won't pass onevar
- // But our modules can
- delete srcHintOptions.onevar;
- grunt.initConfig({
- pkg: grunt.file.readJSON("package.json"),
- dst: readOptionalJSON("dist/.destination.json"),
- compare_size: {
- files: [ "dist/jquery.js", "dist/jquery.min.js" ],
- options: {
- compress: {
- gz: function( contents ) {
- return gzip.zip( contents, {} ).length;
- }
- },
- cache: "dist/.sizecache.json"
- }
- },
- selector: {
- destFile: "src/selector-sizzle.js",
- apiFile: "src/sizzle-jquery.js",
- srcFile: "src/sizzle/dist/sizzle.js"
- },
- build: {
- all: {
- dest: "dist/jquery.js",
- src: [
- "src/intro.js",
- "src/core.js",
- { flag: "sizzle", src: "src/selector-sizzle.js", alt: "src/selector-native.js" },
- "src/callbacks.js",
- "src/deferred.js",
- "src/support.js",
- "src/data.js",
- "src/queue.js",
- "src/attributes.js",
- "src/event.js",
- "src/traversing.js",
- "src/manipulation.js",
- { flag: "wrap", src: "src/wrap.js" },
- { flag: "css", src: "src/css.js" },
- "src/serialize.js",
- { flag: "event-alias", src: "src/event-alias.js" },
- { flag: "ajax", src: "src/ajax.js" },
- { flag: "ajax/script", src: "src/ajax/script.js", needs: ["ajax"] },
- { flag: "ajax/jsonp", src: "src/ajax/jsonp.js", needs: [ "ajax", "ajax/script" ] },
- { flag: "ajax/xhr", src: "src/ajax/xhr.js", needs: ["ajax"] },
- { flag: "effects", src: "src/effects.js", needs: ["css"] },
- { flag: "offset", src: "src/offset.js", needs: ["css"] },
- { flag: "dimensions", src: "src/dimensions.js", needs: ["css"] },
- { flag: "deprecated", src: "src/deprecated.js" },
- "src/exports.js",
- "src/outro.js"
- ]
- }
- },
- jshint: {
- dist: {
- src: [ "dist/jquery.js" ],
- options: srcHintOptions
- },
- grunt: {
- src: [ "Gruntfile.js" ],
- options: {
- jshintrc: ".jshintrc"
- }
- },
- tests: {
- // TODO: Once .jshintignore is supported, use that instead.
- // issue located here: https://github.com/gruntjs/grunt-contrib-jshint/issues/1
- src: [ "test/data/{test,testinit,testrunner}.js", "test/unit/**/*.js" ],
- options: {
- jshintrc: "test/.jshintrc"
- }
- }
- },
- testswarm: {
- tests: "ajax attributes callbacks core css data deferred dimensions effects event manipulation offset queue selector serialize support traversing Sizzle".split(" ")
- },
- watch: {
- files: [ "<%= jshint.grunt.src %>", "<%= jshint.tests.src %>", "src/**/*.js" ],
- tasks: "dev"
- },
- "pre-uglify": {
- all: {
- files: {
- "dist/jquery.pre-min.js": [ "dist/jquery.js" ]
- },
- options: {
- banner: "\n\n\n\n\n\n\n\n\n\n" + // banner line size must be preserved
- "/*! jQuery v<%= pkg.version %> | " +
- "(c) 2005, 2013 jQuery Foundation, Inc. | " +
- "jquery.org/license\n" +
- "//@ sourceMappingURL=jquery.min.map\n" +
- "*/\n"
- }
- }
- },
- uglify: {
- all: {
- files: {
- "dist/jquery.min.js": [ "dist/jquery.pre-min.js" ]
- },
- options: {
- // Keep our hard-coded banner
- preserveComments: "some",
- sourceMap: "dist/jquery.min.map",
- sourceMappingURL: "jquery.min.map",
- report: "min",
- beautify: {
- ascii_only: true
- },
- compress: {
- hoist_funs: false,
- join_vars: false,
- loops: false,
- unused: false
- },
- mangle: {
- // saves some bytes when gzipped
- except: [ "undefined" ]
- }
- }
- }
- },
- "post-uglify": {
- all: {
- files: {
- "dist/jquery.min.map.tmp": [ "dist/jquery.min.map" ],
- "dist/jquery.min.js.tmp": [ "dist/jquery.min.js" ]
- },
- options: {
- tempFiles: [ "dist/jquery.min.map.tmp", "dist/jquery.min.js.tmp", "dist/jquery.pre-min.js" ]
- }
- }
- }
- });
- 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;
- });
- // TODO: create separate job for git/git2 so we can do different browsersets
- 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: "popular-no-old-ie"
- }, function( err, passed ) {
- if ( err ) {
- grunt.log.error( err );
- }
- done( passed );
- }
- );
- });
- grunt.registerTask( "selector", "Build Sizzle-based selector module", function() {
- var cfg = grunt.config("selector"),
- name = cfg.destFile,
- sizzle = {
- api: grunt.file.read( cfg.apiFile ),
- src: grunt.file.read( cfg.srcFile )
- },
- compiled, parts;
- /**
- sizzle-jquery.js -> sizzle between "EXPOSE" blocks,
- replace define & window.Sizzle assignment
- // EXPOSE
- if ( typeof define === "function" && define.amd ) {
- define(function() { return Sizzle; });
- } else {
- window.Sizzle = Sizzle;
- }
- // EXPOSE
- Becomes...
- Sizzle.attr = jQuery.attr;
- jQuery.find = Sizzle;
- jQuery.expr = Sizzle.selectors;
- jQuery.expr[":"] = jQuery.expr.pseudos;
- jQuery.unique = Sizzle.uniqueSort;
- jQuery.text = Sizzle.getText;
- jQuery.isXMLDoc = Sizzle.isXML;
- jQuery.contains = Sizzle.contains;
- */
- // Break into 3 pieces
- parts = sizzle.src.split("// EXPOSE");
- // Replace the if/else block with api
- parts[1] = sizzle.api;
- // Rejoin the pieces
- compiled = parts.join("");
- grunt.verbose.writeln("Injected " + cfg.apiFile + " into " + cfg.srcFile);
- // Write concatenated source to file, and ensure newline-only termination
- grunt.file.write( name, compiled.replace( /\x0d\x0a/g, "\x0a" ) );
- // Fail task if errors were logged.
- if ( this.errorCount ) {
- return false;
- }
- // Otherwise, print a success message.
- grunt.log.writeln( "File '" + name + "' created." );
- });
- // Special "alias" task to make custom build creation less grawlix-y
- grunt.registerTask( "custom", function() {
- var done = this.async(),
- args = [].slice.call(arguments),
- modules = args.length ? args[0].replace(/,/g, ":") : "";
- // Translation example
- //
- // grunt custom:+ajax,-dimensions,-effects,-offset
- //
- // Becomes:
- //
- // grunt build:*:*:+ajax:-dimensions:-effects:-offset
- grunt.log.writeln( "Creating custom build...\n" );
- grunt.util.spawn({
- grunt: true,
- args: [ "build:*:*:" + modules, "pre-uglify", "uglify", "dist" ]
- }, function( err, result ) {
- if ( err ) {
- grunt.verbose.error();
- done( err );
- return;
- }
- grunt.log.writeln( result.stdout.replace("Done, without errors.", "") );
- done();
- });
- });
- // Special concat/build task to handle various jQuery build requirements
- grunt.registerMultiTask(
- "build",
- "Concatenate source (include/exclude modules with +/- flags), embed date/version",
- function() {
- // Concat specified files.
- var compiled = "",
- modules = this.flags,
- optIn = !modules["*"],
- explicit = optIn || Object.keys(modules).length > 1,
- name = this.data.dest,
- src = this.data.src,
- deps = {},
- excluded = {},
- version = grunt.config( "pkg.version" ),
- excluder = function( flag, needsFlag ) {
- // optIn defaults implicit behavior to weak exclusion
- if ( optIn && !modules[ flag ] && !modules[ "+" + flag ] ) {
- excluded[ flag ] = false;
- }
- // explicit or inherited strong exclusion
- if ( excluded[ needsFlag ] || modules[ "-" + flag ] ) {
- excluded[ flag ] = true;
- // explicit inclusion overrides weak exclusion
- } else if ( excluded[ needsFlag ] === false &&
- ( modules[ flag ] || modules[ "+" + flag ] ) ) {
- delete excluded[ needsFlag ];
- // ...all the way down
- if ( deps[ needsFlag ] ) {
- deps[ needsFlag ].forEach(function( subDep ) {
- modules[ needsFlag ] = true;
- excluder( needsFlag, subDep );
- });
- }
- }
- };
- // 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)
- src.forEach(function( filepath ) {
- var flag = filepath.flag;
- if ( flag ) {
- excluder(flag);
- // check for dependencies
- if ( filepath.needs ) {
- deps[ flag ] = filepath.needs;
- filepath.needs.forEach(function( needsFlag ) {
- excluder( flag, needsFlag );
- });
- }
- }
- });
- // append excluded modules to version
- if ( Object.keys( excluded ).length ) {
- version += " -" + Object.keys( excluded ).join( ",-" );
- // set pkg.version to version with excludes, so minified file picks it up
- grunt.config.set( "pkg.version", version );
- }
- // conditionally concatenate source
- src.forEach(function( filepath ) {
- var flag = filepath.flag,
- specified = false,
- omit = false,
- messages = [];
- if ( flag ) {
- if ( excluded[ flag ] !== undefined ) {
- messages.push([
- ( "Excluding " + flag ).red,
- ( "(" + filepath.src + ")" ).grey
- ]);
- specified = true;
- omit = !filepath.alt;
- if ( !omit ) {
- flag += " alternate";
- filepath.src = filepath.alt;
- }
- }
- if ( excluded[ flag ] === undefined ) {
- messages.push([
- ( "Including " + flag ).green,
- ( "(" + filepath.src + ")" ).grey
- ]);
- // If this module was actually specified by the
- // builder, then set the flag to include it in the
- // output list
- if ( modules[ "+" + flag ] ) {
- specified = true;
- }
- }
- filepath = filepath.src;
- // Only display the inclusion/exclusion list when handling
- // an explicit list.
- //
- // Additionally, only display modules that have been specified
- // by the user
- if ( explicit && specified ) {
- messages.forEach(function( message ) {
- grunt.log.writetableln( [ 27, 30 ], message );
- });
- }
- }
- if ( !omit ) {
- compiled += grunt.file.read( filepath );
- }
- });
- // Embed Version
- // Embed Date
- compiled = compiled.replace( /@VERSION/g, version )
- // 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 );
- // Fail task if errors were logged.
- if ( this.errorCount ) {
- return false;
- }
- // Otherwise, print a success message.
- grunt.log.writeln( "File '" + name + "' created." );
- });
- // Process files for distribution
- grunt.registerTask( "dist", function() {
- var stored, flags, paths, fs, 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
- fs = require( "fs" );
- 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( /^\/\*!(?:.|\n)*?\*\/\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() {
- var fs = require( "fs" );
- 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-update-submodules");
- grunt.loadNpmTasks("grunt-contrib-watch");
- grunt.loadNpmTasks("grunt-contrib-jshint");
- grunt.loadNpmTasks("grunt-contrib-uglify");
- // Default grunt
- grunt.registerTask( "default", [ "update_submodules", "selector", "build:*:*", "jshint", "pre-uglify", "uglify", "post-uglify", "dist:*", "compare_size" ] );
- // Short list as a high frequency watch task
- grunt.registerTask( "dev", [ "selector", "build:*:*", "jshint" ] );
- };
|