/*jslint node: true */ 'use strict'; var pkg = require('./package.json'); var serveStatic = require('serve-static'); var serveIndex = require('serve-index'); //Using exclusion patterns slows down Grunt significantly //instead of creating a set of patterns like '**/*.js' and '!**/node_modules/**' //This method assumes the website root is in the app folder. // //omitDirPrefix: If true, refrains from adding 'app/' to each of the patterns. var createFolderGlobs = function (fileTypePatterns, omitDirPrefix) { var ignore = ['yarn_components', 'tzdata']; //FOR DEBUGGING //console.log('\ncreateFolderGlobs()...'); //Convert fileTypePatterns to array fileTypePatterns = Array.isArray(fileTypePatterns) ? fileTypePatterns : [fileTypePatterns]; var fs = require('fs'); //FOR DEBUGGING //console.log('File Type Patterns (Computed): ' + fileTypePatterns); //Change from the grunt dir to the app dir var gruntDir = process.cwd(); process.chdir('app'); //FOR DEBUGGING //console.log('CWD: ' + process.cwd()); //Modify var returnPatterns = fs.readdirSync(process.cwd()) .map(function (file) { //console.log('Examining: ' + file); //If this is a file, or a folder in the ignore list if (ignore.indexOf(file) !== -1 || file.indexOf('.') === 0 || !fs.lstatSync(file).isDirectory()) { //FOR DEBUGGING //console.log('Ignoring: ' + file); return null; } else { //FOR DEBUGGING //console.log('--> Accepting: ' + file); return fileTypePatterns.map(function (pattern) { return file + '/**/' + pattern; }); } }) .filter(function (patterns) { return patterns; }) .concat(fileTypePatterns); //Change back from app dir to grunt dir process.chdir(gruntDir); if (!omitDirPrefix) { //Prepend 'app/' to each filter item var patternsString = returnPatterns.toString(); var csvArray = patternsString.split(','); returnPatterns = csvArray.map(function (pattern) { return 'app/' + pattern; }); } //FOR DEBUGGING //console.log('RESULT: ' + returnPatterns); return returnPatterns; }; //end function createFolderGlobs() module.exports = function (grunt) { require('time-grunt')(grunt); // load all grunt tasks with jit loading require('jit-grunt')(grunt, { protractor: 'grunt-protractor-runner', configureProxies: 'grunt-connect-proxy2', dom_munger: 'grunt-dom-munger2', ngtemplates: 'grunt-angular-templates' }); // Project configuration. grunt.initConfig({ connect: { //Default options for all connect targets options: { port: 9000, hostname: '127.0.0.1', base: 'app/', livereload: 35729, open: 'http://127.0.0.1:9000', middleware: function (connect, options) { if (!Array.isArray(options.base)) { options.base = [options.base]; } // Setup the proxy var middlewares = [require('grunt-connect-proxy2/lib/utils').proxyRequest]; // Serve static files. options.base.forEach(function (base) { middlewares.push(serveStatic(base)); }); // Make directory browse-able. var directory = options.directory || options.base[options.base.length - 1]; middlewares.push(serveIndex(directory)); return middlewares; } }, main: { //See https://github.com/drewzboto/grunt-connect-proxy proxies: [ { context: ['/rest', '/portal'], host: 'localhost', port: 8080, ws: true, } ] }, dist: { options: { base: 'dist/' }, proxies: [ { context: ['/rest', '/portal'], host: 'localhost', port: 8080 } ] } }, watch: { main: { options: { livereload: true, spawn: false, debounceDelay: 250 }, files: [createFolderGlobs(['*.js', '*.less', '*.html', '*.css']), '!_SpecRunner.html', '!.grunt', '!*-spec.js'], tasks: [] }, dist: { options: { livereload: true, spawn: false }, files: ['app.full.min.css', 'app.full.min.js', 'index.html'], tasks: [] //all the tasks are run dynamically during the watch event handler } }, jshint: { main: { options: { jshintrc: '.jshintrc' }, src: [createFolderGlobs('*.js'), '*.js', '!app/common/generated_code/**', '!Gruntfile.js', '!assets.js', '!webpack.config.js', '!yarn_file_copy.js'] } }, csslint: { main: { options: { csslintrc: '.csslintrc' }, src: ['<%= dom_munger.data.applintcss %>', '!app/compiled-less/**', '!app/yarn_components/**'] } }, lesslint: { main: { options: { csslint: { csslintrc: '.csslintrc' } }, src: ['app/*.less', '!app/compiled-less/**', '!app/yarn_components/**'] } }, clean: { before: { src: ['dist', 'temp', 'test_results'] }, after: { src: ['temp'] }, cleanDistTest: { src: ['distTestFolder'] } }, less: { production: { files: [{ cwd: 'app', src: ['**/*.less', '!yarn_components/**'], dest: 'app/compiled-less/', filter: 'isFile', expand: true, ext: '.less.css' }] } }, ngtemplates: { main: { options: { module: pkg.name, htmlmin: '<%= htmlmin.main.options %>' }, src: [createFolderGlobs('*.html', true), '!index.html', '!_SpecRunner.html'], dest: 'temp/templates.js', cwd: 'app/' } }, copy: { main: { files: [ {cwd: 'app', src: ['<%= dom_munger.data.appjsCopyList %>','<%= dom_munger.data.appcssCopyList %>'], dest: 'dist/', filter: 'isFile', expand: true}, {cwd: 'app', src: ['yarn_components/**/*.woff'], dest: 'dist/', filter: 'isFile', expand: true}, {cwd: 'app', src: ['yarn_components/**/*.woff2'], dest: 'dist/', filter: 'isFile', expand: true}, {cwd: 'app', src: ['yarn_components/**/*.eot'], dest: 'dist/', filter: 'isFile', expand: true}, {cwd: 'app', src: ['yarn_components/**/*.svg'], dest: 'dist/', filter: 'isFile', expand: true}, {cwd: 'app', src: ['yarn_components/**/*.ttf'], dest: 'dist/', filter: 'isFile', expand: true}, {cwd: 'app', src: ['yarn_components/**/*.png'], dest: 'dist/', filter: 'isFile', expand: true}, {cwd: 'app', src: ['yarn_components/**/*.jpg'], dest: 'dist/', filter: 'isFile', expand: true}, {cwd: 'app', src: ['yarn_components/**/*.gif'], dest: 'dist/', filter: 'isFile', expand: true}, {cwd: 'app', src: ['images/**/*.svg'], dest: 'dist/', filter: 'isFile', expand: true}, {cwd: 'app', src: ['images/**/*.png'], dest: 'dist/', filter: 'isFile', expand: true}, {cwd: 'app', src: ['images/**/*.jpg'], dest: 'dist/', filter: 'isFile', expand: true}, {cwd: 'app', src: ['images/**/*.gif'], dest: 'dist/', filter: 'isFile', expand: true}, {cwd: 'app', src: ['fonts/google-md-icons/**'], dest:'dist/', filter: 'isFile', expand: true}, {cwd: 'app', src: ['favicon.ico'], dest: 'dist/', filter: 'isFile', expand: true}, {cwd: 'app', src: ['robots.txt'], dest: 'dist/', filter: 'isFile', expand: true}, {cwd: 'app', src: ['404.html'], dest: 'dist/', filter: 'isFile', expand: true} ] }, //Prepares a copy of the dist folder and inserts unit tests for running //unit tests on the distribution build. prepareDistTestFolder: { files: [ //Copy the dist folder's contents to the war. //Path is relative to src/main/webapp/grunt {cwd: 'dist', src: ['**'], dest: 'distTestFolder', filter: 'isFile', expand: true}, {cwd: 'app', src: ['**/*-spec.js'], dest: 'distTestFolder', filter: 'isFile', expand: true} ] } }, dom_munger: { //jshint ignore: line read: { options: { read: [ {selector: 'script[data-build!="exclude"]', attribute: 'src', writeto: 'appjs', isPath: true}, {selector: 'link[type="text/css"][data-build!="exclude"]', attribute: 'href', writeto: 'appcss', isPath: true}, {selector: 'script[data-remove="exclude"]', attribute: 'src', writeto: 'appjsCopyList'}, {selector: 'link[type="text/css"][data-remove="exclude"]', attribute: 'href', writeto: 'appcssCopyList'}, {selector: 'script[data-karma-tests!="exclude"]', attribute: 'src', writeto: 'applintjs', isPath: true}, {selector: 'link[type="text/css"]', attribute: 'href', writeto: 'applintcss', isPath: true} ] }, src: 'app/index.html' }, update: { options: { remove: ['script[data-remove!="exclude"]', 'link[data-remove!="exclude"]'], append: [ {selector: 'body', html: ''}, {selector: 'head', html: ''} ] }, src: 'app/index.html', dest: 'dist/index.html' } }, cssmin: { options: { target: 'dist/'//Eg. Changes '../relative/url/path/in/bower/components' to 'app/yarn_components/...' //root: 'app' //Eg. changes '../relative/url/path/in/bower/components' to '/yarn_components...', but the starting slash causes problems }, main: { files: { 'dist/app.full.min.css': [ 'temp/**/*.less.css', '<%= dom_munger.data.appcss %>' ] } } }, concat: { main: { src: ['<%= dom_munger.data.appjs %>', '<%= ngtemplates.main.dest %>'], dest: 'temp/app.full.js' } }, ngAnnotate: { main: { src: 'temp/app.full.js', dest: 'temp/app.full.js' } }, uglify: { main: { files: { 'dist/app.full.min.js': ['temp/app.full.js'] } }, pretty: { options: { beautify: true, preserveComments: 'all', sourceMap: true, sourceMapName: 'dist/app.full.min.js.map' }, files: { 'dist/app.full.min.js': ['temp/app.full.js'] } } }, html2js: { options: { base: '.', module: 'ui-templates', rename: function (modulePath) { var moduleName = modulePath.replace('app/common/partials/ui-bootstrap-tpls/', '').replace('.html', ''); return 'template' + '/' + moduleName + '.html'; } }, main: { src: ['app/common/partials/ui-bootstrap-tpls/**/*.html'], dest: 'app/common/generated_code/ui-templates.js' } }, htmlmin: { main: { options: { collapseBooleanAttributes: true, collapseWhitespace: true, removeAttributeQuotes: true, removeComments: true, removeEmptyAttributes: true, removeScriptTypeAttributes: true, removeStyleLinkTypeAttributes: true }, files: { 'dist/index.html': 'dist/index.html' } } }, concurrent: { build: [ 'onlyBuild' ], /** * Note: The multiCapabilities feature of protractor does not seem compatible * with grunt concurrent, unless everything passes perfectly */ test: { tasks: [ 'unitTests', ] }, serve: { tasks: [ 'unitTests', 'onlyServe' ], options: { logConcurrentOutput: true } } } //end concurrent }); //end grunt.initConfig //Runs the linters through our code grunt.registerTask('lintAll', [ 'dom_munger:read', 'jshint', 'lesslint', 'csslint']); grunt.registerTask('unitTests', [ 'lintAll', 'dom_munger:read', ]); /** * Used by the concurrent task to split up build task. */ grunt.registerTask('onlyBuild', [ 'clean:before', 'dom_munger', 'less', //Compiles less files to css. Creates one css file for each less file in a respective folder in temp/path/name.css 'ngtemplates', //creates temp/templates.js that maps templates safely for minification 'cssmin', //minifies css into dist/app.full.min.css 'concat', 'ngAnnotate', //Makes AngularJS files minification safe. Reads temp/app.full.js. Writes results to temp/app.full.js 'uglify:main', //Minifies and uglifies 'copy:main', 'htmlmin', 'clean:after' ]); /** * Used by the concurrent task to split up serve task. */ grunt.registerTask('onlyServe', [ 'configureProxies:main', 'connect:main', 'watch:main' ]); //Builds and runs a live watch server using the distribution build. grunt.registerTask('serveDist', ['build', 'connect:dist', 'watch:dist']); //Runs a test on the distribution build. //NOTE: this currently fails with an undefined tinyMCE variable // grunt.registerTask('testDist', [ 'build', 'copy:prepareDistTestFolder', 'clean:cleanDistTest' ]); //############################################################################ //############################################################################ // The tasks below are intended for public command line use //############################################################################ //############################################################################ /** Makes the distribution build */ grunt.registerTask('build', ['concurrent:build']); /** Runs a live watch server proxied between port 9000 and 8080 */ grunt.registerTask('serve', ['concurrent:serve']); /** Re converts the html/js templates. */ grunt.registerTask('runHTML2js', ['html2js']); /** Runs the test task, including lint, unit tests, and e2e tests */ grunt.registerTask('test', ['concurrent:test']); /** By default, we run the serve task */ grunt.registerTask('default', ['serve']); /** Compiles project's less files into css. */ grunt.registerTask('makeLess', ['dom_munger', 'less']); };