UNPKG

gaf-mobile

Version:

GAF mobile Web site

894 lines (830 loc) 22.9 kB
'use strict'; // # Globbing // for performance reasons we're only matching one level down: // 'test/spec/{,*/}*.js' // use this if you want to recursively match all subfolders: // 'test/spec/**/*.js' module.exports = function(grunt) { // Load grunt tasks automatically require('load-grunt-tasks')(grunt); // Time how long tasks take. Can help when optimizing build times require('time-grunt')(grunt); // For generating service worker var swPrecache = require('sw-precache'), path = require('path'); // Rewrite middleware to get pushstate working in dev mode var modRewrite = require('connect-modrewrite'); var distDir = 'www'; // Define the configuration for all the tasks grunt.initConfig({ // Project settings yeoman: { // configurable paths app: require('./bower.json').appPath || 'app', dist: distDir }, // Watches files for changes and runs tasks based on the changed files watch: { js: { files: [ '<%= yeoman.app %>/**/*.js', 'fl-angular/src/**/*.js' ], tasks: ['newer:jshint:all'], options: { livereload: true } }, less: { files: [ '<%= yeoman.app %>/styles/**/*.less', '<%= yeoman.app %>/components/**/*.less' ], tasks: ['less'] }, gruntfile: { files: ['Gruntfile.js'] }, ngconstant: { files: ['config/*.json'], tasks: ['ngconstant'] }, bower: { files: ['bower.json'], tasks: ['exec:bowerInstall'] }, livereload: { options: { livereload: '<%= connect.options.livereload %>' }, files: [ '<%= yeoman.app %>/**/*.html', '.tmp/styles/**/*.css', '<%= yeoman.app %>/images/**/*.{png,jpg,jpeg,gif,webp,svg}' ] }, ngdocs: { files: [ 'app/scripts/**/*.js', 'app/components/**/*.js', 'README.md' ], tasks: ['build-docs'], options: { livereload: true } }, patternLibrary: { files: [ 'pattern-library/components/**/*.html', 'pattern-library/components/**/*.md', 'pattern-library/**/*.html' ], tasks: ['build-kss'], options: { livereload: true } } }, // The actual grunt server settings connect: { options: { port: 9000, livereload: 35729, hostname: '*' }, livereload: { options: { open: grunt.option('open') ? 'http://m-local.freelancer.com:9000' : false, base: [ '.tmp', '<%= yeoman.app %>' ], middleware: function(connect, options) { var middlewares = []; middlewares.push(modRewrite([ '^\/(?!bower_components|favicon\.ico|service-worker\.js|manifest\.json|apple-app-site-association|images|scripts|styles|views|components|translations).*$ /index.html [L]' // jshint ignore:line ])); options.base.forEach(function(base) { middlewares.push(connect.static(base)); }); return middlewares; } } }, dist: { options: { port: 9000, open: grunt.option('open') ? 'http://m-local.freelancer.com:9000' : false, base: '<%= yeoman.dist %>', middleware: function(connect, options) { var middlewares = []; middlewares.push(modRewrite([ '^\/(?!bower_components|favicon\.ico|service-worker\.js|manifest\.json|apple-app-site-association|images|scripts|styles|support|views|components|translations).*$ /index.html [L]' // jshint ignore:line ])); middlewares.push(connect.static(options.base)); return middlewares; } } }, test: { options: { port: 9001, base: [ '.tmp', 'test', '<%= yeoman.app %>' ] } }, ngdocs: { options: { port: 1111, base: 'docs/', open: true } }, patternLibrary: { options: { port: 1112, base: 'pattern-library/dest/', open: true } } }, // Make sure code styles are up to par and there are no obvious mistakes jshint: { options: { jshintrc: '.jshintrc', reporter: require('jshint-stylish') }, src: [ 'Gruntfile.js', '<%= yeoman.app %>/scripts/**/*.js', '!<%= yeoman.app %>/scripts/ext/**/*.js', '<%= yeoman.app %>/components/**/*.js' ] }, // Empties folders to start fresh clean: { dist: { files: [{ dot: true, src: [ '.tmp', '<%= yeoman.dist %>/*', '!<%= yeoman.dist %>/.git*' ] }] }, server: '.tmp', docs: 'docs', coverage: 'test/coverage' }, // Add vendor prefixed styles autoprefixer: { options: { browsers: ['last 2 versions'] }, dist: { files: [{ expand: true, cwd: '.tmp/styles/', src: '{,*/}*.css', dest: '.tmp/styles/' }] } }, // Automatically inject Bower components into the app, i.e. fill // the bower:js tags inside index.html wiredep: { app: { src: '<%= yeoman.app %>/index.html', options: { includeSelf: true, ignorePath: '<%= yeoman.app %>/' } } }, // Renames files for browser caching purposes rev: { dist: { files: { src: [ '<%= yeoman.dist %>/scripts/**/*.js', '<%= yeoman.dist %>/components/**/*.js', '<%= yeoman.dist %>/styles/**/*.{css,eot,ttf,woff}', '<%= yeoman.dist %>/images/**/*.{png,jpg,jpeg,gif,webp,svg}', '<%= yeoman.dist %>/components/**/*.{png,jpg,jpeg,gif,webp,svg}' ] } } }, // Reads HTML for usemin blocks to enable smart builds that automatically // concat, minify and revision files. Creates configurations in memory so // additional tasks can operate on them // Default flow: 'js': ['concat', 'uglifyjs'], 'css': ['concat', 'cssmin'] useminPrepare: { html: '<%= yeoman.app %>/**/*.html', options: { dest: '<%= yeoman.dist %>' } }, // Generate source maps uglify: { options: { sourceMap: true, sourceMapIncludeSources: true, sourceMapLocation: 'https://m.freelancer.com/', sourceMapRoot: '<%= yeoman.dist %>' } }, // Performs rewrites based on rev and the useminPrepare configuration usemin: { html: ['<%= yeoman.dist %>/**/*.html'], htmlflSvg: ['<%= yeoman.dist %>/**/*.html'], htmli18nSrc: ['<%= yeoman.dist %>/**/*.html'], css: ['<%= yeoman.dist %>/styles/{,*/}*.css'], json: ['<%= yeoman.dist %>/**/*.json'], options: { assetsDirs: ['<%= yeoman.dist %>', '<%= yeoman.dist %>/images'], patterns: { htmlflSvg: [ [/<fl-svg[^\>]*[^\>\S]+src=['"]([^'"\)#]+)(#.+)?["']/gm, 'Replacing fl-svg references'] ], htmli18nSrc: [ [/<img[^\>]*[^\>\S]+i18n-src=['"]([^'"\)#]+)(#.+)?["']/gm, 'Replacing i18n-src references'] ] } } }, // Lossless compression of image files imagemin: { dist: { files: [{ expand: true, cwd: '<%= yeoman.app %>/', src: [ 'images/**/*.{png,jpg,jpeg,gif}', 'components/**/*.{png,jpg,jpeg,gif}' ], dest: '<%= yeoman.dist %>/' }] } }, htmlmin: { dist: { options: { removeComments: true, collapseWhitespace: true, conservativeCollapse: true, collapseBooleanAttributes: true, removeCommentsFromCDATA: true, minifyJS: true, keepClosingSlash: true, // needed for inline SVG processScripts: ['text/ng-template'] }, files: [{ expand: true, cwd: '<%= yeoman.dist %>', src: [ '*.html', 'views/**/*.html', 'components/**/*.html' ], dest: '<%= yeoman.dist %>' }] } }, // Show compression report after CSS minification by usemin cssmin: { options: { report: 'min' } }, // Allow the use of non-minsafe AngularJS files. Automatically makes it // minsafe compatible so Uglify does not destroy the ng references ngAnnotate: { dist: { files: [{ expand: true, cwd: '.tmp/concat/scripts', src: '*.js', dest: '.tmp/concat/scripts' }] } }, // Copies remaining files to places other tasks can use copy: { dist: { files: [{ expand: true, dot: true, cwd: '<%= yeoman.app %>', dest: '<%= yeoman.dist %>', src: [ 'favicon.ico', 'manifest.json', 'robots.txt', 'apple-app-site-association', 'sitemap.xml', '*.html', 'views/**/*.html', 'components/**/*.{html,svg}', 'images/**/*.{webp,svg}', // not minified 'styles/fonts/**/*.{eot,ttf,woff}' ] }, { expand: true, src: 'support/service-worker/*.js', dest: '<%= yeoman.dist %>' }] }, // Copy the README.md file to /docs folder and rename it to index.ngdoc // so that it's available as the home page for our documentation website ngdocIndex: { files: [{ expand: true, cwd: '.', src: ['README.md'], dest: 'docs/', rename: function(dest) { return dest + 'index.ngdoc'; } }] } }, // Add @ngdoc directives to index.ngdoc so it can be imported properly file_append: { default_options: { files: [function() { return { prepend: '@ngdoc overview\n@name API Reference\n@description\n\n', input: 'docs/index.ngdoc' }; }] } }, ngdocs: { options: { html5Mode: false, title: 'mobile-web', }, api: { src: ['app/scripts/**/*.js', 'app/components/**/*.js', 'docs/index.ngdoc'], title: 'Mobile Site Documentation' } }, // Run some tasks in parallel to speed up the build process concurrent: { server: [ 'less:server' ], test: [ 'less:dist' ], dist: [ 'less:dist', 'imagemin' ] }, // Compiles Less files to CSS less: { server: { files: { '.tmp/styles/main.css': '<%= yeoman.app %>/styles/base.less' } }, dist: { options: { cleancss: true }, files: { '.tmp/styles/main.css': '<%= yeoman.app %>/styles/base.less' } }, patternLibrary: { files: { 'pattern-library/public/styles/main.css': '<%= yeoman.app %>/styles/base.less' } } }, // Generates a 'configs' angular module with configuration constants ngconstant: { options: { dest: '<%= yeoman.app %>/scripts/config.js', name: 'configs', wrap: '"use strict";\n\n {%= __ngModule %}' }, dist: { constants: function() { return './config/' + grunt.config('config') + '.json'; } }, test: { options: { dest: '<%= yeoman.app %>/scripts/config.js', }, constants: './config/production.json' } }, // Inject configs into index.html replace: { dist: { options: { patterns: [{ match: 'SENTRY_DSN_URL', replacement: function() { return grunt.file.readJSON('./config/' + grunt.config('config') + '.json').SENTRY_DSN_URL; }, expression: false }, { match: 'VERSION', replacement: '<%= gitinfo.local.branch.current.shortSHA.toUpperCase() %>', // jshint ignore:line expression: false }] }, files: { '<%= yeoman.dist %>/index.html': '<%= yeoman.dist %>/index.html' } } }, // Karma Test Runner. Run the Unit and Intergration (e2e) tests. karma: { options: { configFile: 'karma.conf.js', files: require('main-bower-files')({ includeSelf: true, includeDev: true }).map(function(file) { return require('path').relative(__dirname, file); }).concat([ 'app/scripts/config.js', 'app/bower_components/' + 'angular-google-places-autocomplete/dist/autocomplete.min.js', 'app/bower_components/**/*.mock.js', 'test/mocks.js', 'app/scripts/**/*.spec.js', 'app/scripts/**/*.mock.js', 'app/scripts/services/**/*.spec.js', // temporary until tests under components/ are fixed 'app/components/hireme-modal/*.spec.js', 'app/components/**/*.html', 'app/views/*.html' ]) }, dev: { autoWatch: true, singleRun: false, reporters: ['mocha'] }, release: { autoWatch: false, singleRun: true, reporters: ['mocha', 'coverage'] }, debug: { autoWatch: true, singleRun: false, browsers: ['Chrome'], preprocessors: [], reporters: ['mocha'] }, nyan: { autoWatch: false, singleRun: true, preprocessors: [], reporters: ['nyan'] } }, // E2e tests runner protractor: { options: { keepAlive: true, }, local: { options: { configFile: 'test/protractor.conf.js' } }, sauce: { options: { configFile: 'test/sauce.conf.js' } } }, // Use that task to execute miscellaneous command lines exec: { rsyncStaging: { cmd: 'rsync -e "ssh -l andrea.gherardi" www/ ' + 'mdevwww.freelancer.com:/var/www/mobile/ -rvz' }, bowerInstall: { cmd: './node_modules/.bin/bower --allow-root install' }, createSymlinks: { cmd: './script/create-symlinks.sh ./fl-angular/src/' }, removeSymlinks: { cmd: './script/remove-symlinks.sh ./fl-angular/src/' } }, // Inline the common templates (menu, primary nav, ...) into the index.html // to limit round trip on app init ng_template: { options: { appDir: '<%= yeoman.dist %>', indexFile: 'index.html', concat: true }, files: [ '<%= yeoman.dist %>/views/menu.html', '<%= yeoman.dist %>/views/main.html', '<%= yeoman.dist %>/components/mobile-footer/mobile-footer.html', '<%= yeoman.dist %>/components/posting-guide/posting-guide.html' ] }, // keep up to date the bower & npm dependencies checkDependencies: { npm: { options: { packageManager: 'npm', install: true } } }, threepioExtract: { pot: { options: { base: '<%= yeoman.app %>' }, files: { 'localization/mobile-web.pot': [ '<%= yeoman.app %>/**/*.html', '!<%= yeoman.app %>/bower_components/**/*', '!<%= yeoman.app %>/views/bid-proposals.html' ] } } }, threepioCompile: { json: { files: [{ expand: true, cwd: 'localization', src: '*.po', dest: '<%= yeoman.app %>/translations/', ext: '.json' }] }, js: { options: { wrap: 'angular.module("threepio").run(function(TranslationCache) {' + ' TranslationCache.put("${lang}", ${dict});' + '});' }, files: [{ expand: true, cwd: 'localization', src: '*.po', dest: '<%= yeoman.app %>/translations/', ext: '.js' }] } }, threepioTranslate: { all: { options: { catalogs: 'localization/*.po', html: { missing: true } }, files: [{ expand: true, cwd: '<%= yeoman.app %>', src: '**/*.html', ext: '.${lang}.html', dest: '<%= yeoman.dist %>' }] } }, coverage: { default: { options: { thresholds: { 'statements': -351, 'branches': -270, 'lines': -350, 'functions': -90 }, dir: 'coverage', root: 'test' } } }, swPrecache: { build: { handleFetch: true, rootDir: '<%= yeoman.dist %>', importScripts: [ 'support/service-worker/idb-helper.js', 'support/service-worker/offline-tracking.js' ] }, dev: { handleFetch: false, rootDir: '<%= yeoman.app %>' } }, kss: { options: { source: 'pattern-library/components', config: 'kss-config.json' }, dist: { files: { 'pattern-library/dest/': [ 'app/styles/' ] } } } }); // Run the app in a local environment, i.e. spawn a web server // and use grunt-livereload to automatically refresh the browser grunt.registerTask('serve', function(target, config) { if (target === 'dist') { if (config) { grunt.warn('In dist mode serve does not accept a config: you need ' + 'to run "build:<config>" first"'); } else { grunt.task.run([ 'proxy', 'connect:dist:keepalive' ]); } } else { if (target && !config) { config = target; } else { // Default to staging config config = 'staging'; } grunt.log.ok('Hello there! You\'re using the "' + config + '" config in dev mode'); grunt.config('config', config); grunt.task.run([ 'clean:server', 'ngconstant:dist', 'checkDependencies:npm', 'exec:bowerInstall', 'wiredep', 'concurrent:server', 'autoprefixer', 'proxy', 'connect:livereload', 'watch' ]); } }); // Build the docs grunt.registerTask('build-docs', [ 'copy:ngdocIndex', 'file_append', 'ngdocs' ]); // Serve the docs grunt.registerTask('docs', [ 'build-docs', 'connect:ngdocs', 'watch:ngdocs' ]); // Update Pattern Library Docs grunt.registerTask('build-kss', [ 'less:patternLibrary', 'kss:dist' ]); // Serve the lib docs grunt.registerTask('styleguide', [ 'build-kss', 'connect:patternLibrary', 'watch:patternLibrary' ]); grunt.registerTask('test', [ 'karma:release', 'coverage' ]); // Default task grunt.registerTask('default', [ 'checkDependencies', 'exec:bowerInstall', 'jshint', 'ngconstant:test', 'build-docs', 'karma:release', 'coverage' ]); // Run by the CI server grunt.registerTask('ci', [ 'default' ]); grunt.registerMultiTask('swPrecache', function() { var done = this.async(); var rootDir = this.data.rootDir; var handleFetch = this.data.handleFetch; var importScripts = this.data.importScripts; // TODO: Pass options instead of hardcoding here var config = { cacheId: 'gaf-mobile', handleFetch: handleFetch, importScripts: importScripts, navigateFallback: '/', staticFileGlobs: [ rootDir + '/styles/**.css', rootDir + '/scripts/**.js', rootDir + '/components/**/*.html', rootDir + '/views/**/*.html', rootDir + '/index.html', // offline page rootDir + '/images/global/*.fl-logo-color-v2.svg', rootDir + '/images/404/*.universe.jpg', rootDir + '/images/404/*.flying-unicorn-sheep.svg' ], stripPrefix: rootDir, verbose: true }; swPrecache.write(path.join(rootDir, 'service-worker.js'), config, function(error) { if (error) { grunt.fail.warn(error); } done(); }); }); // Define browsertest & unittest // Add JSHint to the build task!! grunt.registerTask('build', 'Build the app', function(config) { if (!config && !grunt.option('config')) { grunt.warn('A valid config must be specified: local,' + ' staging, or production (e.g., build:local)'); } grunt.config('config', config); grunt.task.run([ 'clean:dist', 'gitinfo', // populate 'gitinfo' config object 'ngconstant:dist', 'checkDependencies:npm', 'exec:bowerInstall', 'wiredep', 'useminPrepare', 'concurrent:dist', 'autoprefixer', 'concat:generated', // usemin flow 'ngAnnotate', 'copy:dist', 'uglify:generated', // usemin flow 'cssmin:generated', // usemin flow 'rev', 'usemin', 'revLocalizedAssets', 'htmlmin', // minify the HTML files & partials 'replace', // inject configs into index.html 'ng_template', // inject common templates (nav, menu) into index.html 'prerenderConfig', // fl-prerender config, 'threepioExtract', // translations 'swPrecache:build' // generate service worker for precaching ]); }); grunt.registerTask('revLocalizedAssets', 'Fix the revision of localized assets', function() { // TODO // For each image (recursive) // For each localized image (next to the image) // Copy the rev of the default language and assign it to the // localized one }); grunt.registerTask('dev', 'Turn on & off dev mode (fl angular libs as ' + 'symlinks)', function(on) { if (on === 'on') { grunt.task.run([ 'exec:createSymlinks' ]); } else if (on === 'off') { grunt.task.run([ 'exec:removeSymlinks', 'exec:bowerInstall' ]); } else { grunt.warn('Do you want to turn "on" (grunt dev:on) or "off" (grunt ' + 'dev:off) the dev mode?'); } }); // Load custom tasks grunt.loadTasks('./tasks'); };