More simplifications fore jQuery#html
[jquery.git] / Gruntfile.js
blobea51d180a81dae722d90e916f7b851bb673e8b29
1 module.exports = function( grunt ) {
3         "use strict";
5         var distpaths = [
6                         "dist/jquery.js",
7                         "dist/jquery.min.map",
8                         "dist/jquery.min.js"
9                 ],
10                 readOptionalJSON = function( filepath ) {
11                         var data = {};
12                         try {
13                                 data = grunt.file.readJSON( filepath );
14                         } catch(e) {}
15                         return data;
16                 };
18         grunt.initConfig({
19                 pkg: grunt.file.readJSON("package.json"),
20                 dst: readOptionalJSON("dist/.destination.json"),
21                 compare_size: {
22                         files: distpaths
23                 },
24                 selector: {
25                         "src/selector.js": [
26                                 "src/sizzle-jquery.js",
27                                 "src/sizzle/sizzle.js"
28                         ]
29                 },
30                 build: {
31                         "dist/jquery.js": [
32                                 "src/intro.js",
33                                 "src/core.js",
34                                 "src/callbacks.js",
35                                 "src/deferred.js",
36                                 "src/support.js",
37                                 "src/data.js",
38                                 "src/queue.js",
39                                 "src/attributes.js",
40                                 "src/event.js",
41                                 "src/selector.js",
42                                 "src/traversing.js",
43                                 "src/manipulation.js",
45                                 { flag: "css", src: "src/css.js" },
46                                 "src/serialize.js",
47                                 { flag: "ajax", src: "src/ajax.js" },
48                                 { flag: "ajax/script", src: "src/ajax/script.js", needs: ["ajax"]  },
49                                 { flag: "ajax/jsonp", src: "src/ajax/jsonp.js", needs: [ "ajax", "ajax/script" ]  },
50                                 { flag: "ajax/xhr", src: "src/ajax/xhr.js", needs: ["ajax"]  },
51                                 { flag: "effects", src: "src/effects.js", needs: ["css"] },
52                                 { flag: "offset", src: "src/offset.js", needs: ["css"] },
53                                 { flag: "dimensions", src: "src/dimensions.js", needs: ["css"] },
54                                 { flag: "deprecated", src: "src/deprecated.js" },
56                                 "src/exports.js",
57                                 "src/outro.js"
58                         ]
59                 },
61                 jshint: {
62                         dist: {
63                                 src: [ "dist/jquery.js" ],
64                                 options: {
65                                         jshintrc: "src/.jshintrc"
66                                 }
67                         },
68                         grunt: {
69                                 src: [ "Gruntfile.js" ],
70                                 options: {
71                                         jshintrc: ".jshintrc"
72                                 }
73                         },
74                         tests: {
75                                 // TODO: Once .jshintignore is supported, use that instead.
76                                 // issue located here: https://github.com/gruntjs/grunt-contrib-jshint/issues/1
77                                 src: [ "test/data/{test,testinit,testrunner}.js", "test/unit/**/*.js" ],
78                                 options: {
79                                         jshintrc: "test/.jshintrc"
80                                 }
81                         }
82                 },
84                 testswarm: {
85                         tests: "ajax attributes callbacks core css data deferred dimensions effects event manipulation offset queue selector serialize support traversing Sizzle".split(" ")
86                 },
88                 watch: {
89                         files: [ "<%= jshint.grunt.src %>", "<%= jshint.tests.src %>", "src/**/*.js" ],
90                         tasks: "dev"
91                 },
93                 uglify: {
94                         all: {
95                                 files: {
96                                         "dist/jquery.min.js": [ "dist/jquery.js" ]
97                                 },
98                                 options: {
99                                         banner: "/*! jQuery v<%= pkg.version %> jquery.com | jquery.org/license */",
100                                         sourceMap: "dist/jquery.min.map",
101                                         beautify: {
102                                                 ascii_only: true
103                                         }
104                                 }
105                         }
106                 }
107         });
109         grunt.registerTask( "testswarm", function( commit, configFile ) {
110                 var jobName,
111                         testswarm = require( "testswarm" ),
112                         testUrls = [],
113                         pull = /PR-(\d+)/.exec( commit ),
114                         config = grunt.file.readJSON( configFile ).jquery,
115                         tests = grunt.config([ this.name, "tests" ]);
117                 if ( pull ) {
118                         jobName = "jQuery pull <a href='https://github.com/jquery/jquery/pull/" +
119                                 pull[ 1 ] + "'>#" + pull[ 1 ] + "</a>";
120                 } else {
121                         jobName = "jQuery commit #<a href='https://github.com/jquery/jquery/commit/" +
122                                 commit + "'>" + commit.substr( 0, 10 ) + "</a>";
123                 }
125                 tests.forEach(function( test ) {
126                         testUrls.push( config.testUrl + commit + "/test/index.html?module=" + test );
127                 });
129                 testswarm({
130                         url: config.swarmUrl,
131                         pollInterval: 10000,
132                         timeout: 1000 * 60 * 30,
133                         done: this.async()
134                 }, {
135                         authUsername: config.authUsername,
136                         authToken: config.authToken,
137                         jobName: jobName,
138                         runMax: config.runMax,
139                         "runNames[]": tests,
140                         "runUrls[]": testUrls,
141                         "browserSets[]": config.browserSets
142                 });
143         });
145         // Build src/selector.js
146         grunt.registerMultiTask( "selector", "Build src/selector.js", function() {
148                 var name = this.file.dest,
149                                 files = this.file.src,
150                                 sizzle = {
151                                         api: grunt.file.read( files[0] ),
152                                         src: grunt.file.read( files[1] )
153                                 },
154                                 compiled, parts;
156                 /**
158                         sizzle-jquery.js -> sizzle between "EXPOSE" blocks,
159                         replace define & window.Sizzle assignment
162                         // EXPOSE
163                         if ( typeof define === "function" && define.amd ) {
164                                 define(function() { return Sizzle; });
165                         } else {
166                                 window.Sizzle = Sizzle;
167                         }
168                         // EXPOSE
170                         Becomes...
172                         Sizzle.attr = jQuery.attr;
173                         jQuery.find = Sizzle;
174                         jQuery.expr = Sizzle.selectors;
175                         jQuery.expr[":"] = jQuery.expr.pseudos;
176                         jQuery.unique = Sizzle.uniqueSort;
177                         jQuery.text = Sizzle.getText;
178                         jQuery.isXMLDoc = Sizzle.isXML;
179                         jQuery.contains = Sizzle.contains;
181                  */
183                 // Break into 3 pieces
184                 parts = sizzle.src.split("// EXPOSE");
185                 // Replace the if/else block with api
186                 parts[1] = sizzle.api;
187                 // Rejoin the pieces
188                 compiled = parts.join("");
190                 grunt.verbose.write("Injected sizzle-jquery.js into sizzle.js");
192                 // Write concatenated source to file
193                 grunt.file.write( name, compiled );
195                 // Fail task if errors were logged.
196                 if ( this.errorCount ) {
197                         return false;
198                 }
200                 // Otherwise, print a success message.
201                 grunt.log.writeln( "File '" + name + "' created." );
202         });
205         // Special "alias" task to make custom build creation less grawlix-y
206         grunt.registerTask( "custom", function() {
207                 var done = this.async(),
208                                 args = [].slice.call(arguments),
209                                 modules = args.length ? args[0].replace(/,/g, ":") : "";
212                 // Translation example
213                 //
214                 //   grunt custom:+ajax,-dimensions,-effects,-offset
215                 //
216                 // Becomes:
217                 //
218                 //   grunt build:*:*:+ajax:-dimensions:-effects:-offset
220                 grunt.log.writeln( "Creating custom build...\n" );
222                 grunt.util.spawn({
223                         cmd: process.platform === "win32" ? "grunt.cmd" : "grunt",
224                         args: [ "build:*:*:" + modules, "uglify", "dist" ]
225                 }, function( err, result ) {
226                         if ( err ) {
227                                 grunt.verbose.error();
228                                 done( err );
229                                 return;
230                         }
232                         grunt.log.writeln( result.stdout.replace("Done, without errors.", "") );
234                         done();
235                 });
236         });
238         // Special concat/build task to handle various jQuery build requirements
239         //
240         grunt.registerMultiTask(
241                 "build",
242                 "Concatenate source (include/exclude modules with +/- flags), embed date/version",
243                 function() {
244                         // Concat specified files.
245                         var compiled = "",
246                                 modules = this.flags,
247                                 optIn = !modules["*"],
248                                 explicit = optIn || Object.keys(modules).length > 1,
249                                 name = this.file.dest,
250                                 src = this.file.srcRaw,
251                                 deps = {},
252                                 excluded = {},
253                                 version = grunt.config( "pkg.version" ),
254                                 excluder = function( flag, needsFlag ) {
255                                         // optIn defaults implicit behavior to weak exclusion
256                                         if ( optIn && !modules[ flag ] && !modules[ "+" + flag ] ) {
257                                                 excluded[ flag ] = false;
258                                         }
260                                         // explicit or inherited strong exclusion
261                                         if ( excluded[ needsFlag ] || modules[ "-" + flag ] ) {
262                                                 excluded[ flag ] = true;
264                                         // explicit inclusion overrides weak exclusion
265                                         } else if ( excluded[ needsFlag ] === false &&
266                                                 ( modules[ flag ] || modules[ "+" + flag ] ) ) {
268                                                 delete excluded[ needsFlag ];
270                                                 // ...all the way down
271                                                 if ( deps[ needsFlag ] ) {
272                                                         deps[ needsFlag ].forEach(function( subDep ) {
273                                                                 modules[ needsFlag ] = true;
274                                                                 excluder( needsFlag, subDep );
275                                                         });
276                                                 }
277                                         }
278                                 };
280                         // append commit id to version
281                         if ( process.env.COMMIT ) {
282                                 version += " " + process.env.COMMIT;
283                         }
285                         // figure out which files to exclude based on these rules in this order:
286                         //  dependency explicit exclude
287                         //  > explicit exclude
288                         //  > explicit include
289                         //  > dependency implicit exclude
290                         //  > implicit exclude
291                         // examples:
292                         //  *                  none (implicit exclude)
293                         //  *:*                all (implicit include)
294                         //  *:*:-css           all except css and dependents (explicit > implicit)
295                         //  *:*:-css:+effects  same (excludes effects because explicit include is trumped by explicit exclude of dependency)
296                         //  *:+effects         none except effects and its dependencies (explicit include trumps implicit exclude of dependency)
297                         src.forEach(function( filepath ) {
298                                 var flag = filepath.flag;
300                                 if ( flag ) {
302                                         excluder(flag);
304                                         // check for dependencies
305                                         if ( filepath.needs ) {
306                                                 deps[ flag ] = filepath.needs;
307                                                 filepath.needs.forEach(function( needsFlag ) {
308                                                         excluder( flag, needsFlag );
309                                                 });
310                                         }
311                                 }
312                         });
314                         // append excluded modules to version
315                         if ( Object.keys( excluded ).length ) {
316                                 version += " -" + Object.keys( excluded ).join( ",-" );
317                                 // set pkg.version to version with excludes, so minified file picks it up
318                                 grunt.config.set( "pkg.version", version );
319                         }
322                         // conditionally concatenate source
323                         src.forEach(function( filepath ) {
324                                 var flag = filepath.flag,
325                                                 specified = false,
326                                                 omit = false,
327                                                 message = "";
329                                 if ( flag ) {
330                                         if ( excluded[ flag ] !== undefined ) {
331                                                 message = ( "Excluding " + flag ).red;
332                                                 specified = true;
333                                                 omit = true;
334                                         } else {
335                                                 message = ( "Including " + flag ).green;
337                                                 // If this module was actually specified by the
338                                                 // builder, then st the flag to include it in the
339                                                 // output list
340                                                 if ( modules[ "+" + flag ] ) {
341                                                         specified = true;
342                                                 }
343                                         }
345                                         // Only display the inclusion/exclusion list when handling
346                                         // an explicit list.
347                                         //
348                                         // Additionally, only display modules that have been specified
349                                         // by the user
350                                         if ( explicit && specified ) {
351                                                 grunt.log.writetableln([ 27, 30 ], [
352                                                         message,
353                                                         ( "(" + filepath.src + ")").grey
354                                                 ]);
355                                         }
357                                         filepath = filepath.src;
358                                 }
360                                 if ( !omit ) {
361                                         compiled += grunt.file.read( filepath );
362                                 }
363                         });
365                         // Embed Version
366                         // Embed Date
367                         compiled = compiled.replace( /@VERSION/g, version )
368                                 .replace( "@DATE", function () {
369                                         var date = new Date();
371                                         // YYYY-MM-DD
372                                         return [
373                                                 date.getFullYear(),
374                                                 date.getMonth() + 1,
375                                                 date.getDate()
376                                         ].join( "-" );
377                                 });
379                         // Write concatenated source to file
380                         grunt.file.write( name, compiled );
382                         // Fail task if errors were logged.
383                         if ( this.errorCount ) {
384                                 return false;
385                         }
387                         // Otherwise, print a success message.
388                         grunt.log.writeln( "File '" + name + "' created." );
389                 });
391         // Process files for distribution
392         grunt.registerTask( "dist", function() {
393                 var flags, paths, stored;
395                 // Check for stored destination paths
396                 // ( set in dist/.destination.json )
397                 stored = Object.keys( grunt.config("dst") );
399                 // Allow command line input as well
400                 flags = Object.keys( this.flags );
402                 // Combine all output target paths
403                 paths = [].concat( stored, flags ).filter(function( path ) {
404                         return path !== "*";
405                 });
407                 // Ensure the dist files are pure ASCII
408                 var fs = require("fs"),
409                         nonascii = false;
411                 distpaths.forEach(function( filename ) {
412                         var text = fs.readFileSync( filename, "utf8" ),
413                         i, c;
414                         if ( text.length !== Buffer.byteLength( text, "utf8" ) ) {
415                                 grunt.log.writeln( filename + ": Non-ASCII characters detected:" );
416                                 for ( i = 0; i < text.length; i++ ) {
417                                         c = text.charCodeAt( i );
418                                         if ( c > 127 ) {
419                                                 grunt.log.writeln( "- position " + i + ": " + c );
420                                                 grunt.log.writeln( "-- " + text.substring( i - 20, i + 20 ) );
421                                                 nonascii = true;
422                                                 break;
423                                         }
424                                 }
425                         }
427                         // Modify map so that it points to files in the same folder;
428                         // see https://github.com/mishoo/UglifyJS2/issues/47
429                         if ( /\.map$/.test( filename ) ) {
430                                 text = text.replace( /"dist\//g, "\"" );
431                                 fs.writeFileSync( filename, text, "utf-8" );
432                         }
434                         // Optionally copy dist files to other locations
435                         paths.forEach(function( path ) {
436                                 var created;
438                                 if ( !/\/$/.test( path ) ) {
439                                         path += "/";
440                                 }
442                                 created = path + filename.replace( "dist/", "" );
443                                 grunt.file.write( created, text );
444                                 grunt.log.writeln( "File '" + created + "' created." );
445                         });
446                 });
448                 return !nonascii;
449         });
451         // Load grunt tasks from NPM packages
452         grunt.loadNpmTasks("grunt-compare-size");
453         grunt.loadNpmTasks("grunt-git-authors");
454         grunt.loadNpmTasks("grunt-update-submodules");
455         grunt.loadNpmTasks("grunt-contrib-watch");
456         grunt.loadNpmTasks("grunt-contrib-jshint");
457         grunt.loadNpmTasks("grunt-contrib-uglify");
459         // Default grunt
460         grunt.registerTask( "default", [ "update_submodules", "selector", "build:*:*", "jshint", "uglify", "dist:*", "compare_size" ] );
462         // Short list as a high frequency watch task
463         grunt.registerTask( "dev", [ "selector", "build:*:*", "jshint" ] );