'use strict'; module.exports = function (grunt) { var path = require('path'); var pkg = grunt.file.readJSON('package.json'); var singleQuoteRe = /^[\s']+|[\s']+$/g; var doubleQuoteRe = /^[\s"]+|[\s"]+$/g; var resourceUrlRe = /url\s*\(\s*(([\'"])?([^\)]+)?\1?)\s*\)/gi; grunt.initConfig({ pkg: pkg, app: { module: 'app', path: 'app', dest: '../web', backend: { path: '../web/protected', pathControllers: '../web/protected/controllers' } }, banner: '/*! <%= pkg.name %> - v<%= pkg.version %> - <%= grunt.template.today("yyyy-mm-dd") %>\n' + '<%= pkg.homepage ? "* " + pkg.homepage + "\\n" : "" %>' + '* Copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.author.name %>\n' + '* License <%= pkg.licenses ? _.pluck(pkg.licenses, "type").join(", ") : pkg.license %> \n*/\n' }); grunt.loadNpmTasks('grunt-usemin'); grunt.config('useminPrepare', { app: { files: [{ src: '<%= app.path %>/site-index-debug.php', dest: '<%= app.dest %>/site-index.php' }], options: { dest: '<%= app.dest %>' } } }); grunt.config('usemin', { app_html: { options: { type: 'html' }, files: [{ src: '<%= app.dest %>/site-index.php' }] }, app_css: { options: { type: 'css' }, files: [{ src: '<%= app.dest %>/*.css' }] } }); grunt.loadNpmTasks('grunt-contrib-clean'); grunt.config('clean', { app: { options: { force: true }, files: [{ dot: true, src: [ '<%= app.dest %>/site-index.php', '<%= app.dest %>/combined.*.js', '<%= app.dest %>/combined.*.css', '<%= app.dest %>/lib', '<%= app.dest %>/translate', '<%= app.dest %>/content', '<%= app.dest %>/template', '<%= app.dest %>/images/*', '!<%= app.dest %>/images/image-not-available.png', '!<%= app.dest %>/images/no-image-ticket.png', '!<%= app.dest %>/.ht*' ] }] }, tmp: { files: [{ dot: true, src: ['.tmp'] }] }, translate: { src: '<%= app.path %>/translate/*.js.bak*' } }); grunt.loadNpmTasks('grunt-contrib-copy'); grunt.config('copy', { app: { files: [ { src: '<%= app.path %>/site-index-debug.php', dest: '<%= app.dest %>/site-index.php' }, { expand: true, cwd: '<%= app.path %>', src: [ 'translate/**/*', 'content/*', 'template/**/*', 'images/**/*', 'lib/angular/i18n/**/*', 'lib/**/*.{png,jpg,jpeg,gif,swf,otf,eot,svg,ttf,woff}' ], dest: '<%= app.dest %>', flatten: false } ] } }); grunt.loadNpmTasks('grunt-angular-templates'); grunt.config('ngtemplates', { app: { files: [{ cwd: '<%= app.path %>', src: ['directive/**/*.html', 'controller/**/*.html'], dest: '.tmp/template.js' }], options: { module: '<%= app.module %>', htmlmin: { collapseWhitespace: false, removeComments: true } } } }); grunt.loadNpmTasks('grunt-contrib-concat'); grunt.config('concat', { generated: { options: { process: function (src, srcPath) { if ('.css' === path.extname(srcPath)) { var dstPath = 'app/combined.css'; src = src.replace(resourceUrlRe, function (str, url, quote) { if (0 === url.indexOf('data:') || -1 !== url.indexOf('://') || '/' === url[0]) { return str; } if (quote) { url = url.replace('"' === quote ? doubleQuoteRe : singleQuoteRe, ''); } else { quote = ''; } var newUrl = path.relative(path.dirname(dstPath), path.join(path.dirname(srcPath), url)); grunt.log.writeln('fixing css url (' + srcPath + ')\n' + url + '\n=> ' + newUrl + '\n'); return 'url(' + quote + newUrl + quote + ')'; }); } return src; } } }, app_templateCache: { options: { separator: '\n' }, files: [{ src: ['.tmp/concat/combined.js', '.tmp/template.js'], dest: '.tmp/concat/combined.js' }] }, banner: { options: { banner: '<%= banner %>' }, files: [{ src: ['<%= app.dest %>/combined.js'], dest: '<%= app.dest %>/combined.js' }, { src: ['<%= app.dest %>/combined.css'], dest: '<%= app.dest %>/combined.css' }] } }); grunt.loadNpmTasks('grunt-filerev'); grunt.config('filerev', { app: { files: [{ src: [ '<%= app.dest %>/combined.js', '<%= app.dest %>/combined.css' ] }] } }); // https://github.com/gruntjs/grunt-contrib-uglify grunt.loadNpmTasks('grunt-contrib-uglify'); grunt.config('uglify', { options: { preserveComments: 'some' } }); // https://github.com/gruntjs/grunt-contrib-cssmin // https://github.com/GoalSmashers/clean-css // todo: use "root" and "relativeTo" options to rebase resource urls grunt.loadNpmTasks('grunt-contrib-cssmin'); grunt.loadNpmTasks('grunt-contrib-htmlmin'); // https://github.com/apidoc/grunt-apidoc grunt.config('apidoc', { backend: { files: [{ src: '<%= app.backend.pathControllers %>/*.php', dest: 'apidoc/' }], options: { debug: true } } }); // todo: implement apidoc task grunt.registerMultiTask('apidoc', 'generate REST API documentation', function () { var apidoc = require("apidoc"); grunt.log.fail('not implemented'); }); // grunt.loadNpmTasks('grunt-jsdoc'); // grunt.loadNpmTasks('grunt-translate-extract'); // grunt.config('translateExtract', { // app: { // src: [ // 'app/controller/**/*.js', // 'app/controller/**/*.html', // 'app/directive/**/*.js', // 'app/directive/**/*.html', // 'app/service/**/*.js' // ], // dest: 'translateExtract', // lang: ['en', 'uk', 'ru'] // } // }); grunt.config('translateSort', { app: { src: '<%= app.path %>/translate/*.js', options: { backup: false, template: "(function () {\n" + " 'use strict';\n\n" + " angular.module('translate.data', [])\n" + " .constant('translateData', {});\n" + "})();" } } }); grunt.registerMultiTask('translateSort', 'sort translations', function () { var vm = require('vm'), path = require('path'); var options = this.options(), files = this.filesSrc; var names = [], translations = [], translateData; var sandbox = { angular: { module: function () { return { constant: function (name, data) { translateData = data; } } } } }; files.forEach(function (filePath) { grunt.log.writeln('processing: ' + filePath); var content = grunt.file.read(filePath); translateData = null; vm.runInNewContext(content, sandbox); names = names.concat(Object.keys(translateData)); translations.push({ path: filePath, data: translateData }); }); names = names.filter(function (v, i, a) { return v && i === a.indexOf(v); }).sort().sort(function (a, b) { a = a.toLowerCase(); b = b.toLowerCase(); if (a === b) return 0; return a < b ? -1 : 1; }); translations.forEach(function (info) { var data = {}; var prefix; names.forEach(function (v, i) { data[v] = v in info.data ? info.data[v] : ''; }); var content = options.template.replace('{}', JSON.stringify(data, null, ' ').replace(/\n/g, '\n ')); if (options.backup) { prefix = '.bak-' + (new Date()).toISOString().substring(0, 19).replace(/[:]/g, '-'); grunt.file.copy(info.path, info.path + prefix); } grunt.file.write(info.path, content); grunt.log.ok('updated translation file: ' + info.path); }); }); grunt.registerTask('build', [ 'clean:tmp', 'clean:app', 'useminPrepare:app', 'ngtemplates:app', 'concat:generated', 'concat:app_templateCache', 'uglify:generated', 'cssmin:generated', 'copy:app', 'concat:banner', 'filerev:app', 'usemin:app_html', 'usemin:app_css', 'clean:tmp' ]); grunt.registerTask('cleanup', ['clean:app']); grunt.registerTask('doc', ['apidoc:backend']); grunt.registerTask('translate', ['translateExtract:app']); grunt.registerTask('default', ['build']); };