Allow executing unit tests during the build phase
[dlux.git] / dlux-web / Gruntfile.js
1 var lrSnippet = require('connect-livereload')();\r
2 var mountFolder = function (connect, dir) {\r
3     return connect.static(require('path').resolve(dir));\r
4 };\r
5 \r
6 module.exports = function ( grunt ) {\r
7 \r
8   /**\r
9    * Load required Grunt tasks. These are installed based on the versions listed\r
10    * in `package.json` when you do `npm install` in this directory.\r
11    */\r
12   grunt.loadNpmTasks('grunt-browserify');\r
13   grunt.loadNpmTasks('grunt-contrib-clean');\r
14   grunt.loadNpmTasks('grunt-contrib-copy');\r
15   grunt.loadNpmTasks('grunt-contrib-jshint');\r
16   grunt.loadNpmTasks('grunt-contrib-concat');\r
17   grunt.loadNpmTasks('grunt-contrib-watch');\r
18   grunt.loadNpmTasks('grunt-contrib-uglify');\r
19   grunt.loadNpmTasks('grunt-conventional-changelog');\r
20   grunt.loadNpmTasks('grunt-bump');\r
21   //grunt.loadNpmTasks('grunt-recess');\r
22   grunt.loadNpmTasks('grunt-shell');\r
23   grunt.loadNpmTasks('grunt-karma');\r
24   grunt.loadNpmTasks('grunt-ng-annotate');\r
25   grunt.loadNpmTasks('grunt-html2js');\r
26   grunt.loadNpmTasks('grunt-contrib-less');\r
27   grunt.loadNpmTasks('grunt-contrib-connect');\r
28   grunt.loadNpmTasks('grunt-open');\r
29   grunt.loadNpmTasks('grunt-replace');\r
30 \r
31   /**\r
32    * Load in our build configuration file.\r
33    */\r
34   var userConfig = require( './build.config.js' );\r
35 \r
36   var envConfig = {\r
37 \r
38       replace: {\r
39           development: {\r
40               options: {\r
41                   patterns: [\r
42                       {\r
43                           json: grunt.file.readJSON('./config/development.json')\r
44                       }\r
45                   ]\r
46               },\r
47               files: [\r
48                   {\r
49                       expand: true,\r
50                       flatten: true,\r
51                       src: ['./config/env.module.js'],\r
52                       dest: 'src/common/config/'\r
53                   }\r
54               ]\r
55           },\r
56           production: {\r
57               options: {\r
58                   patterns: [\r
59                       {\r
60                           json: grunt.file.readJSON('./config/production.json')\r
61                       }\r
62                   ]\r
63               },\r
64               files: [\r
65                   {\r
66                       expand: true,\r
67                       flatten: true,\r
68                       src: ['./config/env.module.js'],\r
69                       dest: 'src/common/config/'\r
70                   }\r
71               ]\r
72           }\r
73       }\r
74   }\r
75 \r
76   /**\r
77    * This is the configuration object Grunt uses to give each plugin its\r
78    * instructions.\r
79    */\r
80   var taskConfig = {\r
81     /**\r
82      * We read in our `package.json` file so we can access the package name and\r
83      * version. It's already there, so we don't repeat ourselves here.\r
84      */\r
85     pkg: grunt.file.readJSON("package.json"),\r
86 \r
87     /**\r
88      * The banner is the comment that is placed at the top of our compiled\r
89      * source files. It is first processed as a Grunt template, where the `<%=`\r
90      * pairs are evaluated based on this very configuration object.\r
91      */\r
92     meta: {\r
93       banner:\r
94         '/**\n' +\r
95         ' * <%= pkg.name %> - v<%= pkg.version %> - <%= grunt.template.today("yyyy-mm-dd") %>\n' +\r
96         ' * <%= pkg.homepage %>\n' +\r
97         ' *\n' +\r
98         ' * Copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.author %>\n' +\r
99         ' * Licensed <%= pkg.licenses.type %> <<%= pkg.licenses.url %>>\n' +\r
100         ' */\n'\r
101     },\r
102 \r
103     /**\r
104      * Creates a changelog on a new version.\r
105      */\r
106     changelog: {\r
107       options: {\r
108         dest: 'CHANGELOG.md',\r
109         template: 'changelog.tpl'\r
110       }\r
111     },\r
112 \r
113     /**\r
114      * Increments the version number, etc.\r
115      */\r
116     bump: {\r
117       options: {\r
118         files: [\r
119           "package.json",\r
120           "bower.json"\r
121         ],\r
122         commit: false,\r
123         commitMessage: 'chore(release): v%VERSION%',\r
124         commitFiles: [\r
125           "package.json",\r
126           "client/bower.json"\r
127         ],\r
128         createTag: false,\r
129         tagName: 'v%VERSION%',\r
130         tagMessage: 'Version %VERSION%',\r
131         push: false,\r
132         pushTo: 'origin'\r
133       }\r
134     },\r
135 \r
136     /**\r
137      * The directories to delete when `grunt clean` is executed.\r
138      */\r
139     clean: [\r
140       '<%= build_dir %>',\r
141       '<%= compile_dir %>'\r
142     ],\r
143 \r
144     /**\r
145      * The `copy` task just copies files from A to B. We use it here to copy\r
146      * our project assets (images, fonts, etc.) and javascripts into\r
147      * `build_dir`, and then to copy the assets to `compile_dir`.\r
148      */\r
149     copy: {\r
150       build_app_assets: {\r
151         files: [\r
152           {\r
153             src: [ '**' ],\r
154             dest: '<%= build_dir %>/assets/',\r
155             cwd: 'src/assets',\r
156             expand: true\r
157           }\r
158        ]\r
159       },\r
160       build_vendor_assets: {\r
161         files: [\r
162           {\r
163             src: [ '<%= vendor_files.assets %>' ],\r
164             dest: '<%= build_dir %>/assets/',\r
165             cwd: '.',\r
166             expand: true,\r
167             flatten: true\r
168           }\r
169        ]\r
170       },\r
171       build_appjs: {\r
172         files: [\r
173           {\r
174             src: [ '<%= app_files.js %>' ],\r
175             dest: '<%= build_dir %>/',\r
176             cwd: '.',\r
177             expand: true\r
178           }\r
179         ]\r
180       },\r
181       copy_template: {\r
182         files: [\r
183           {\r
184             src: ['<%= app_files.templates %>'],\r
185             dest: '<%= build_dir %>/',\r
186             cwd: '.',\r
187             expand: true\r
188           }\r
189         ]\r
190       },\r
191       build_vendorjs: {\r
192         files: [\r
193           {\r
194             src: [ '<%= vendor_files.js %>' ],\r
195             dest: '<%= build_dir %>/',\r
196             cwd: '.',\r
197             expand: true\r
198           }\r
199         ]\r
200       },\r
201       build_vendorimages: {\r
202         files: [\r
203           {\r
204             src: [ '<%= vendor_files.images %>' ],\r
205             dest: '<%= build_dir %>/',\r
206             cwd: '.',\r
207             expand: true\r
208           }\r
209         ]\r
210       },\r
211       build_vendorcss: {\r
212         files: [\r
213           {\r
214             src: [ '<%= vendor_files.css %>' ],\r
215             dest: '<%= build_dir %>',\r
216             cwd: '.',\r
217             expand: true\r
218           }\r
219         ]\r
220       },\r
221       compile_assets: {\r
222         files: [\r
223           {\r
224             src: [ '**' ],\r
225             dest: '<%= compile_dir %>/assets',\r
226             cwd: '<%= build_dir %>/assets',\r
227             expand: true\r
228           }\r
229         ]\r
230       },\r
231 \r
232       compile_font: {\r
233         files: [\r
234           {\r
235             src: [ '**' ],\r
236             dest: '<%= compile_dir %>/font',\r
237             cwd: '<%= build_dir %>/font',\r
238             expand: true\r
239           }\r
240         ]\r
241       }\r
242     },\r
243     browserify: {\r
244         dist: {\r
245             src: ['src/app/graph/index.js'],\r
246             dest: 'src/assets/js/graphRenderer.js',\r
247             options: {\r
248               browserifyOptions: {\r
249                 standalone: 'DLUX'\r
250               }\r
251             }\r
252         }\r
253     },\r
254 \r
255     /**\r
256      * `grunt concat` concatenates multiple source files into a single file.\r
257      */\r
258     concat: {\r
259       /**\r
260        * The `build_css` target concatenates compiled CSS and vendor CSS\r
261        * together.\r
262        */\r
263       build_css: {\r
264         src: [\r
265           '<%= vendor_files.css %>',\r
266           '<%= build_dir %>/assets/<%= pkg.name %>-<%= pkg.version %>.css'\r
267         ],\r
268         dest: '<%= build_dir %>/assets/<%= pkg.name %>-<%= pkg.version %>.css'\r
269       },\r
270       /**\r
271        * The `compile_js` target is the concatenation of our application source\r
272        * code and all specified vendor source code into a single file.\r
273        */\r
274       compile_js: {\r
275         options: {\r
276           banner: '<%= meta.banner %>'\r
277         },\r
278         src: [\r
279           '<%= vendor_files.js %>',\r
280           'module.prefix',\r
281           '<%= build_dir %>/src/**/*.js',\r
282           '<%= html2js.common.dest %>',\r
283           '<%= html2js.app.dest %>',\r
284           'module.suffix'\r
285         ],\r
286         dest: '<%= compile_dir %>/assets/<%= pkg.name %>-<%= pkg.version %>.js'\r
287       }\r
288     },\r
289 \r
290     /**\r
291      * `ng-min` annotates the sources before minifying. That is, it allows us\r
292      * to code without the array syntax.\r
293      */\r
294     ngAnnotate: {\r
295       options: {\r
296         singleQuotes:true\r
297       },\r
298       app: {\r
299         files: [\r
300           {\r
301             src: [ '<%= app_files.js %>' ],\r
302             cwd: '<%= build_dir %>',\r
303             dest: '<%= build_dir %>',\r
304             expand: true\r
305           }\r
306         ]\r
307       }\r
308     },\r
309 \r
310     /**\r
311      * Minify the sources!\r
312      */\r
313     uglify: {\r
314       compile: {\r
315         options: {\r
316           banner: '<%= meta.banner %>'\r
317         },\r
318         files: {\r
319           '<%= concat.compile_js.dest %>': '<%= concat.compile_js.dest %>'\r
320         }\r
321       }\r
322     },\r
323 \r
324       /**\r
325        * `less` less plugin handles the LESS compilation and minification automatically\r
326        * this has been changed to the LESS plugin from recess plugin above because of\r
327        * out of memory issues with the original plugin.\r
328        */\r
329 \r
330       less: {\r
331           development: {\r
332               options: {\r
333                   paths: ["assets/css"],\r
334                   compress: false,\r
335                   syncImport: true,\r
336                   strictImports: true\r
337               },\r
338               files: {\r
339                   '<%= build_dir %>/assets/<%= pkg.name %>-<%= pkg.version %>.css': '<%= app_files.less %>'\r
340                   }\r
341           },\r
342           production: {\r
343               options: {\r
344                   paths: ["assets/css"],\r
345                   compress: true,\r
346                   cleancss: true\r
347               },\r
348               files: {\r
349                   '<%= build_dir %>/assets/<%= pkg.name %>-<%= pkg.version %>.css': '<%= app_files.less %>'\r
350               }\r
351           }\r
352       },\r
353 \r
354     /**\r
355      * `jshint` defines the rules of our linter as well as which files we\r
356      * should check. This file, all javascript sources, and all our unit tests\r
357      * are linted based on the policies listed in `options`. But we can also\r
358      * specify exclusionary patterns by prefixing them with an exclamation\r
359      * point (!); this is useful when code comes from a third party but is\r
360      * nonetheless inside `src/`.\r
361      */\r
362     jshint: {\r
363       src: [\r
364         '<%= app_files.js %>'\r
365       ],\r
366       test: [\r
367         '<%= app_files.jsunit %>'\r
368       ],\r
369       gruntfile: [\r
370         'OriginalGruntfile.js'\r
371       ],\r
372       options: {\r
373         curly: true,\r
374         immed: true,\r
375         newcap: true,\r
376         noarg: true,\r
377         sub: true,\r
378         boss: true,\r
379         eqnull: true\r
380       },\r
381       globals: {}\r
382     },\r
383 \r
384 \r
385     /**\r
386      * HTML2JS is a Grunt plugin that takes all of your template files and\r
387      * places them into JavaScript files as strings that are added to\r
388      * AngularJS's template cache. This means that the templates too become\r
389      * part of the initial payload as one JavaScript file. Neat!\r
390      */\r
391     html2js: {\r
392       /**\r
393        * These are the templates from `src/app`.\r
394        */\r
395       app: {\r
396         options: {\r
397           base: 'src/app'\r
398         },\r
399         src: [ '<%= app_files.atpl %>' ],\r
400         dest: '<%= build_dir %>/templates-app.js'\r
401       },\r
402 \r
403       /**\r
404        * These are the templates from `src/common`.\r
405        */\r
406       common: {\r
407         options: {\r
408           base: 'src/common'\r
409         },\r
410         src: [ '<%= app_files.ctpl %>' ],\r
411         dest: '<%= build_dir %>/templates-common.js'\r
412       }\r
413     },\r
414 \r
415     /**\r
416      * The Karma configurations.\r
417      */\r
418     karma: {\r
419       options: {\r
420         configFile: '<%= build_dir %>/karma-unit.js'\r
421       },\r
422       unit: {\r
423         runnerPort: 9102,\r
424         background: true,\r
425         port: 9877 // IMPORTANT!\r
426       },\r
427       continuous: {\r
428         singleRun: true,\r
429         browsers: ['PhantomJS']\r
430       }\r
431     },\r
432 \r
433     /**\r
434      * The `index` task compiles the `index.html` file as a Grunt template. CSS\r
435      * and JS files co-exist here but they get split apart later.\r
436      */\r
437     index: {\r
438 \r
439       /**\r
440        * During development, we don't want to have wait for compilation,\r
441        * concatenation, minification, etc. So to avoid these steps, we simply\r
442        * add all script files directly to the `<head>` of `index.html`. The\r
443        * `src` property contains the list of included files.\r
444        */\r
445       build: {\r
446         dir: '<%= build_dir %>',\r
447         src: [\r
448           '<%= html2js.common.dest %>',\r
449           '<%= html2js.app.dest %>',\r
450           '<%= vendor_files.css %>',\r
451           '<%= build_dir %>/assets/<%= pkg.name %>-<%= pkg.version %>.css'\r
452         ]\r
453       },\r
454 \r
455       /**\r
456        * When it is time to have a completely compiled application, we can\r
457        * alter the above to include only a single JavaScript and a single CSS\r
458        * file. Now we're back!\r
459        */\r
460       compile: {\r
461         dir: '<%= compile_dir %>',\r
462         src: [\r
463           '<%= concat.compile_js.dest %>',\r
464           '<%= concat.build_css.dest %>'\r
465           //'<%= recess.compile.dest %>'\r
466         ]\r
467       }\r
468     },\r
469 \r
470     /**\r
471      * This task compiles the karma template so that changes to its file array\r
472      * don't have to be managed manually.\r
473      */\r
474     karmaconfig: {\r
475       unit: {\r
476         dir: '<%= build_dir %>',\r
477         src: [\r
478           '<%= vendor_files.js %>',\r
479           '<%= html2js.app.dest %>',\r
480           '<%= html2js.common.dest %>',\r
481           '<%= app_files.js_common %>',\r
482           '<%= app_files.js_app %>',\r
483           '<%= app_files.jsunit %>'\r
484         ]\r
485       }\r
486     },\r
487      connect: {\r
488       livereload: {\r
489         options: {\r
490           port: 9000,\r
491           hostname: '0.0.0.0',\r
492           middleware: function (connect) {\r
493             return [\r
494               mountFolder(connect, 'build'),\r
495               lrSnippet\r
496             ];\r
497           }\r
498         }\r
499       },\r
500       dev: {\r
501         options: {\r
502           hostname: '0.0.0.0',\r
503           port: 9000,\r
504           base: 'build'\r
505         }\r
506       },\r
507       prod: {\r
508         options: {\r
509           port: 9001,\r
510           base: 'bin',\r
511           keepalive: true\r
512         }\r
513       }\r
514     },\r
515     open: {\r
516       dev: {\r
517         path: 'http://127.0.0.1:9000/'\r
518       },\r
519       prod: {\r
520         path: 'http://127.0.0.1:9001/'\r
521       }\r
522     },\r
523     /**\r
524      * And for rapid development, we have a watch set up that checks to see if\r
525      * any of the files listed below change, and then to execute the listed\r
526      * tasks when they do. This just saves us from having to type "grunt" into\r
527      * the command-line every time we want to see what we're working on; we can\r
528      * instead just leave "grunt watch" running in a background terminal. Set it\r
529      * and forget it, as Ron Popeil used to tell us.\r
530      *\r
531      * But we don't need the same thing to happen for all the files.\r
532      */\r
533     delta: {\r
534       /**\r
535        * By default, we want the Live Reload to work for all tasks; this is\r
536        * overridden in some tasks (like this file) where browser resources are\r
537        * unaffected. It runs by default on port 35729, which your browser\r
538        * plugin should auto-detect.\r
539        */\r
540       options: {\r
541         livereload: true\r
542       },\r
543 \r
544       /**\r
545        * When the Gruntfile changes, we just want to lint it. In fact, when\r
546        * your Gruntfile changes, it will automatically be reloaded!\r
547        */\r
548       gruntfile: {\r
549         files: 'OriginalGruntfile.js',\r
550         tasks: [ 'jshint:gruntfile' ],\r
551         options: {\r
552           livereload: false\r
553         }\r
554       },\r
555 \r
556       /**\r
557        * When our JavaScript source files change, we want to run lint them and\r
558        * run our unit tests.\r
559        */\r
560       jssrc: {\r
561         files: [\r
562           '<%= app_files.js %>'\r
563         ],\r
564         tasks: [ 'jshint:src', 'karma:unit:run', 'copy:build_appjs' ]\r
565       },\r
566 \r
567       /**\r
568        * When assets are changed, copy them. Note that this will *not* copy new\r
569        * files, so this is probably not very useful.\r
570        */\r
571       assets: {\r
572         files: [\r
573           'src/assets/**/*'\r
574         ],\r
575         tasks: [ 'copy:build_app_assets' ]\r
576       },\r
577 \r
578       /**\r
579        * When index.html changes, we need to compile it.\r
580        */\r
581       html: {\r
582         files: [ '<%= app_files.html %>' ],\r
583         tasks: [ 'index:build' ]\r
584       },\r
585 \r
586       /**\r
587        * When our templates change, we only rewrite the template cache.\r
588        */\r
589       tpls: {\r
590         files: [\r
591           '<%= app_files.atpl %>',\r
592           '<%= app_files.ctpl %>'\r
593         ],\r
594         tasks: [ 'html2js' ]\r
595       },\r
596 \r
597       /**\r
598        * When the CSS files change, we need to compile and minify them.\r
599        */\r
600       less: {\r
601         files: [ 'src/**/*.less' ],\r
602         tasks: [ 'less:development' ]\r
603       },\r
604 \r
605       /**\r
606        * When a JavaScript unit test file changes, we only want to lint it and\r
607        * run the unit tests. We don't want to do any live reloading.\r
608        */\r
609       jsunit: {\r
610         files: [\r
611           '<%= app_files.jsunit %>'\r
612         ],\r
613         tasks: [ 'jshint:test', 'karma:unit:run' ],\r
614         options: {\r
615           livereload: false\r
616         }\r
617       }\r
618     },\r
619     shell : {\r
620       requirejs: {\r
621         command: "node node_modules/requirejs/bin/r.js -o optimize.js"\r
622       }\r
623     }\r
624   };\r
625 \r
626   grunt.initConfig( grunt.util._.extend( taskConfig, userConfig, envConfig ) );\r
627 \r
628   /**\r
629    * In order to make it safe to just compile or copy *only* what was changed,\r
630    * we need to ensure we are starting from a clean, fresh build. So we rename\r
631    * the `watch` task to `delta` (that's why the configuration var above is\r
632    * `delta`) and then add a new task called `watch` that does a clean build\r
633    * before watching for changes.\r
634    */\r
635   grunt.renameTask( 'watch', 'delta' );\r
636   grunt.registerTask( 'watch', [ 'build', 'karma:unit', 'delta' ] );\r
637 \r
638   grunt.registerTask('live', ['build', 'connect:dev', 'delta']);\r
639   /**\r
640    * The default task is to build and compile.\r
641    */\r
642   grunt.registerTask( 'default', [ 'compile' ] );\r
643 \r
644   /**\r
645    * The `build` task gets your app ready to run for development and testing.\r
646    */\r
647   grunt.registerTask( 'common', [\r
648       'clean', 'html2js', 'jshint', 'less:development',\r
649       'concat:build_css', 'browserify:dist', 'copy:build_app_assets', 'copy:build_vendor_assets',\r
650       'copy:build_appjs', 'copy:copy_template', 'copy:build_vendorimages', 'copy:build_vendorjs', 'copy:build_vendorcss', 'karmaconfig', 'index:build'\r
651   ]);\r
652 \r
653   grunt.registerTask( 'build', ['replace:development', 'common', 'karma:continuous']);\r
654 \r
655   /**\r
656    * The `compile` task gets your app ready for deployment by concatenating and\r
657    * minifying your code.\r
658    */\r
659   grunt.registerTask( 'compile', ['replace:production', 'common', 'karma:continuous', 'ngAnnotate', 'shell:requirejs']);\r
660 \r
661   /**\r
662    * A utility function to get all app JavaScript sources.\r
663    */\r
664   function filterForJS ( files ) {\r
665     return files.filter( function ( file ) {\r
666       return file.match( /\.js$/ );\r
667     });\r
668   }\r
669 \r
670   /**\r
671    * A utility function to get all app CSS sources.\r
672    */\r
673   function filterForCSS ( files ) {\r
674     return files.filter( function ( file ) {\r
675       return file.match( /\.css$/ );\r
676     });\r
677   }\r
678 \r
679   /**\r
680    * The index.html template includes the stylesheet and javascript sources\r
681    * based on dynamic names calculated in this Gruntfile. This task assembles\r
682    * the list into variables for the template to use and then runs the\r
683    * compilation.\r
684    */\r
685   grunt.registerMultiTask( 'index', 'Process index.html template', function () {\r
686     var dirRE = new RegExp( '^('+grunt.config('build_dir')+'|'+grunt.config('compile_dir')+')\/', 'g' );\r
687     var jsFiles = filterForJS( this.filesSrc ).map( function ( file ) {\r
688       return file.replace( dirRE, '' );\r
689     });\r
690     var cssFiles = filterForCSS( this.filesSrc ).map( function ( file ) {\r
691       return file.replace( dirRE, '' );\r
692     });\r
693 \r
694     grunt.file.copy('src/index.html', this.data.dir + '/index.html', {\r
695       process: function ( contents, path ) {\r
696         return grunt.template.process( contents, {\r
697           data: {\r
698             scripts: jsFiles,\r
699             styles: cssFiles,\r
700             version: grunt.config( 'pkg.version' )\r
701           }\r
702         });\r
703       }\r
704     });\r
705   });\r
706 \r
707   /**\r
708    * In order to avoid having to specify manually the files needed for karma to\r
709    * run, we use grunt to manage the list for us. The `karma/*` files are\r
710    * compiled as grunt templates for use by Karma. Yay!\r
711    */\r
712   grunt.registerMultiTask( 'karmaconfig', 'Process karma config templates', function () {\r
713     var jsFiles = filterForJS( this.filesSrc );\r
714 \r
715     grunt.file.copy( 'karma/karma-unit.tpl.js', grunt.config( 'build_dir' ) + '/karma-unit.js', {\r
716       process: function ( contents, path ) {\r
717         return grunt.template.process( contents, {\r
718           data: {\r
719             scripts: jsFiles\r
720           }\r
721         });\r
722       }\r
723     });\r
724   });\r
725 \r
726 };\r