Update Sizzle. Fixes #11109.
[jquery.git] / grunt.js
blob4493e083f753effba18f903ec42e24707597534d
1 /**
2  * Resources
3  *
4  * https://gist.github.com/2489540
5  *
6  */
8 /*global config:true, task:true*/
9 module.exports = function( grunt ) {
11         // readOptionalJSON
12         // by Ben Alman
13         // https://gist.github.com/2876125
14         function readOptionalJSON( filepath ) {
15                 var data = {};
16                 try {
17                         data = grunt.file.readJSON(filepath);
18                         grunt.log.write( "Reading data from " + filepath + "..." ).ok();
19                 } catch(e) {}
20                 return data;
21         }
23         var task = grunt.task;
24         var file = grunt.file;
25         var utils = grunt.utils;
26         var log = grunt.log;
27         var verbose = grunt.verbose;
28         var fail = grunt.fail;
29         var option = grunt.option;
30         var config = grunt.config;
31         var template = grunt.template;
32         var distpaths = [
33                 "dist/jquery.js",
34                 "dist/jquery.min.js"
35         ];
37         grunt.initConfig({
38                 pkg: "<json:package.json>",
39                 dst: readOptionalJSON("dist/.destination.json"),
40                 meta: {
41                         banner: "/*! jQuery v@<%= pkg.version %> jquery.com | jquery.org/license */"
42                 },
43                 compare_size: {
44                         files: distpaths
45                 },
46                 selector: {
47                         "src/selector.js": [
48                                 "src/sizzle-jquery.js",
49                                 "src/sizzle/sizzle.js"
50                         ]
51                 },
52                 build: {
53                         "dist/jquery.js": [
54                                 "src/intro.js",
55                                 "src/core.js",
56                                 "src/callbacks.js",
57                                 "src/deferred.js",
58                                 "src/support.js",
59                                 "src/data.js",
60                                 "src/queue.js",
61                                 "src/attributes.js",
62                                 "src/event.js",
63                                 "src/selector.js",
64                                 "src/traversing.js",
65                                 "src/manipulation.js",
66                                 { flag: "css", src: "src/css.js" },
67                                 { flag: "ajax", src: "src/ajax.js" },
68                                 { flag: "ajax/jsonp", src: "src/ajax/jsonp.js", needs: [ "ajax", "ajax/script" ]  },
69                                 { flag: "ajax/script", src: "src/ajax/script.js", needs: ["ajax"]  },
70                                 { flag: "ajax/xhr", src: "src/ajax/xhr.js", needs: ["ajax"]  },
71                                 { flag: "effects", src: "src/effects.js", needs: ["css"] },
72                                 { flag: "offset", src: "src/offset.js", needs: ["css"] },
73                                 { flag: "dimensions", src: "src/dimensions.js", needs: ["css"] },
74                                 "src/exports.js",
75                                 "src/outro.js"
76                         ]
77                 },
78                 min: {
79                         "dist/jquery.min.js": [ "<banner>", "dist/jquery.js" ]
80                 },
81                 lint: {
82                         files: [ "grunt.js", "dist/jquery.js" ]
83                 },
84                 qunit: {
85                         files: "test/index.html"
86                 },
87                 watch: {
88                         files: [ "<config:lint.files>", "src/**/*.js" ],
89                         tasks: "dev"
90                 },
91                 jshint: {
92                         options: {
93                                 evil: true,
94                                 browser: true,
95                                 wsh: true,
96                                 eqnull: true,
97                                 expr: true,
98                                 curly: true,
99                                 trailing: true,
100                                 undef: true,
101                                 smarttabs: true,
102                                 predef: [
103                                         "define",
104                                         "DOMParser",
105                                         "__dirname"
106                                 ],
107                                 maxerr: 100
108                         },
109                         globals: {
110                                 jQuery: true,
111                                 global: true,
112                                 module: true,
113                                 exports: true,
114                                 require: true,
115                                 file: true,
116                                 log: true,
117                                 console: true
118                         }
119                 },
120                 uglify: {}
121         });
123         // Default grunt.
124         grunt.registerTask( "default", "submodules selector build:*:* lint min dist:* compare_size" );
126         // Short list as a high frequency watch task
127         grunt.registerTask( "dev", "selector build:*:* lint" );
129         // Load the "compare_size" task from NPM packages
130         grunt.loadNpmTasks("grunt-compare-size");
132         grunt.registerTask( "testswarm", function( commit, configFile ) {
133                 var testswarm = require( "testswarm" ),
134                         testUrls = [],
135                         config = grunt.file.readJSON( configFile ).jquery;
136                 var tests = "ajax attributes callbacks core css data deferred dimensions effects event manipulation offset queue selector support traversing".split( " " );
137                 tests.forEach(function( test ) {
138                         testUrls.push( config.testUrl + commit + "/test/index.html?module=" + test );
139                 });
140                 testswarm({
141                         url: config.swarmUrl,
142                         pollInterval: 10000,
143                         timeout: 1000 * 60 * 30,
144                         done: this.async()
145                 }, {
146                         authUsername: config.authUsername,
147                         authToken: config.authToken,
148                         jobName: 'jQuery commit #<a href="https://github.com/jquery/jquery/commit/' + commit + '">' + commit.substr( 0, 10 ) + '</a>',
149                         runMax: config.runMax,
150                         "runNames[]": tests,
151                         "runUrls[]": testUrls,
152                         "browserSets[]": ["popular"]
153                 });
154         });
156         // Build src/selector.js
157         grunt.registerMultiTask( "selector", "Build src/selector.js", function() {
159                 var name = this.file.dest,
160                                 files = this.file.src,
161                                 sizzle = {
162                                         api: file.read( files[0] ),
163                                         src: file.read( files[1] )
164                                 },
165                                 compiled;
167                 // sizzle-jquery.js -> sizzle after "EXPOSE", replace window.Sizzle
168                 compiled = sizzle.src.replace( "window.Sizzle = Sizzle;", sizzle.api );
169                 verbose.write("Injected sizzle-jquery.js into sizzle.js");
171                 // Write concatenated source to file
172                 file.write( name, compiled );
174                 // Fail task if errors were logged.
175                 if ( this.errorCount ) {
176                         return false;
177                 }
179                 // Otherwise, print a success message.
180                 log.writeln( "File '" + name + "' created." );
181         });
184         // Special "alias" task to make custom build creation less grawlix-y
185         grunt.registerTask( "custom", function() {
186                 var done = this.async(),
187                                 args = [].slice.call(arguments),
188                                 modules = args.length ? args[0].replace(/,/g, ":") : "";
191                 // Translation example
192                 //
193                 //   grunt custom:+ajax,-dimensions,-effects,-offset
194                 //
195                 // Becomes:
196                 //
197                 //   grunt build:*:*:-ajax:-dimensions:-effects:-offset
199                 grunt.log.writeln( "Creating custom build...\n" );
201                 grunt.utils.spawn({
202                         cmd: "grunt",
203                         args: [ "build:*:*:" + modules ]
204                 }, function( err, result ) {
205                         if ( err ) {
206                                 grunt.verbose.error();
207                                 done( err );
208                                 return;
209                         }
211                         grunt.log.writeln( result.replace("Done, without errors.", "") );
213                         done();
214                 });
215         });
217         // Special concat/build task to handle various jQuery build requirements
218         //
219         grunt.registerMultiTask(
220                 "build",
221                 "Concatenate source (include/exclude modules with +/- flags), embed date/version",
222                 function() {
223                         // Concat specified files.
224                         var i,
225                                 compiled = "",
226                                 modules = this.flags,
227                                 explicit = Object.keys(modules).length > 1,
228                                 optIn = !modules["*"],
229                                 name = this.file.dest,
230                                 excluded = {},
231                                 excluder = function( flag, needsFlag ) {
232                                         // explicit > implicit, so set this first and let it be overridden by explicit
233                                         if ( optIn && !modules[ flag ] && !modules[ "+" + flag ] ) {
234                                                 excluded[ flag ] = false;
235                                         }
237                                         if ( excluded[ needsFlag ] || modules[ "-" + flag ] ) {
238                                                 // explicit exclusion from flag or dependency
239                                                 excluded[ flag ] = true;
240                                         } else if ( modules[ "+" + flag ] && ( excluded[ needsFlag ] === false ) ) {
241                                                 // explicit inclusion from flag or dependency overriding a weak inclusion
242                                                 delete excluded[ needsFlag ];
243                                         }
244                                 };
247                         // figure out which files to exclude based on these rules in this order:
248                         //  explicit > implicit (explicit also means a dependency/dependent that was explicit)
249                         //  exclude > include
250                         // examples:
251                         //  *:                 none (implicit exclude)
252                         //  *:*                all (implicit include)
253                         //  *:*:-effects       all except effects (explicit > implicit)
254                         //  *:*:-css           all except css and its deps (explicit)
255                         //  *:*:-css:+effects  all except css and its deps (explicit exclude from dep. trumps explicit include)
256                         //  *:+effects         none except effects and its deps (explicit include from dep. trumps implicit exclude)
257                         this.file.src.forEach(function( filepath ) {
258                                 var flag = filepath.flag;
260                                 if ( flag ) {
262                                         excluder(flag);
264                                         // check for dependencies
265                                         if ( filepath.needs ) {
266                                                 filepath.needs.forEach(function( needsFlag ) {
267                                                         excluder( flag, needsFlag );
268                                                 });
269                                         }
270                                 }
271                         });
273                         // conditionally concatenate source
274                         this.file.src.forEach(function( filepath ) {
275                                 var flag = filepath.flag,
276                                                 specified = false,
277                                                 message = "";
279                                 if ( flag ) {
280                                         if ( excluded[ flag ] !== undefined ) {
281                                                 message = ( "Excluding " + flag ).red;
282                                                 specified = true;
283                                         } else {
284                                                 message = ( "Including " + flag ).green;
286                                                 // If this module was actually specified by the
287                                                 // builder, then st the flag to include it in the
288                                                 // output list
289                                                 if ( modules[ "+" + flag ] ) {
290                                                         specified = true;
291                                                 }
292                                         }
294                                         // Only display the inclusion/exclusion list when handling
295                                         // an explicit list.
296                                         //
297                                         // Additionally, only display modules that have been specified
298                                         // by the user
299                                         if ( explicit && specified ) {
300                                                 grunt.log.writetableln([ 27, 30 ], [
301                                                         message,
302                                                         ( "(" + filepath.src + ")").grey
303                                                 ]);
304                                         }
306                                         filepath = filepath.src;
307                                 }
309                                 compiled += file.read( filepath );
310                         });
312                         // Embed Date
313                         // Embed Version
314                         compiled = compiled.replace( "@DATE", new Date() )
315                                 .replace( "@VERSION", config("pkg.version") );
317                         // Write concatenated source to file
318                         file.write( name, compiled );
320                         // Fail task if errors were logged.
321                         if ( this.errorCount ) {
322                                 return false;
323                         }
325                         // Otherwise, print a success message.
326                         log.writeln( "File '" + name + "' created." );
327                 });
329         grunt.registerTask( "submodules", function() {
330                 var done = this.async();
332                 grunt.verbose.write( "Updating submodules..." );
334                 // TODO: migrate remaining `make` to grunt tasks
335                 //
336                 grunt.utils.spawn({
337                         cmd: "make"
338                 }, function( err, result ) {
339                         if ( err ) {
340                                 grunt.verbose.error();
341                                 done( err );
342                                 return;
343                         }
345                         grunt.log.writeln( result );
347                         done();
348                 });
349         });
351         // Allow custom dist file locations
352         grunt.registerTask( "dist", function() {
353                 var flags, paths, stored;
355                 // Check for stored destination paths
356                 // ( set in dist/.destination.json )
357                 stored = Object.keys( config("dst") );
359                 // Allow command line input as well
360                 flags = Object.keys( this.flags );
362                 // Combine all output target paths
363                 paths = [].concat( stored, flags ).filter(function( path ) {
364                         return path !== "*";
365                 });
368                 // Proceed only if there are actual
369                 // paths to write to
370                 if ( paths.length ) {
372                         // 'distpaths' is declared at the top of the
373                         // module.exports function scope. It is an array
374                         // of default files that jQuery creates
375                         distpaths.forEach(function( filename ) {
376                                 paths.forEach(function( path ) {
377                                         var created;
379                                         if ( !/\/$/.test( path ) ) {
380                                                 path += "/";
381                                         }
383                                         created = path + filename.replace( "dist/", "" );
385                                         if ( !/^\//.test( path ) ) {
386                                                 log.error( "File '" + created + "' was NOT created." );
387                                                 return;
388                                         }
390                                         file.write( created, file.read(filename) );
392                                         log.writeln( "File '" + created + "' created." );
393                                 });
394                         });
395                 }
396         });