module.exports = function(grunt) { 'use strict'; require('load-grunt-tasks')(grunt); var path = require('path'); var fs = require('fs'); var pkg = require('./package'); var proj_namespace = path.join(pkg.description, pkg.name, pkg.version, '/'); var ASSETS_URL = 'http://assets.dwstatic.com/'+ proj_namespace; var ipAddress = require('network-address')(); var get_files = require('grunt-adiejs-static/lib/get_files').get_files, get_data = require('grunt-adiejs-static/lib/data').get_data, get_layout = require('grunt-adiejs-static/lib/layout').get_layout, render_file = require('grunt-adiejs-static/lib/render').render_file, write_file = require('grunt-adiejs-static/lib/write_file').write_file, get_helper_functions = require('grunt-adiejs-static/lib/get_helper_functions').get_helper_functions, ejs_static = require('grunt-adiejs-static/lib/ejs_static'), _ = require('grunt-adiejs-static/node_modules/underscore'), middleware_directory = require('grunt-contrib-connect/node_modules/connect/lib/middleware/directory'), accepts = require('grunt-contrib-connect/node_modules/connect/node_modules/serve-index/node_modules/accepts'), url = require('url'); var renderTpl = function (req, res, next){ var reqPathName = decodeURIComponent(url.parse(req.originalUrl).pathname), reqFileName = reqPathName.substring(reqPathName.lastIndexOf('/')+1), localPathName = path.resolve('src/', reqPathName.substring(1)), renderedFile; fs.readFile(localPathName, function(err, file) { if (err) {return next();} var options = { path_to_data: 'src/data/config.json', file_extension: '.html', underscore: true }, config_cover = ejs_static.get_files(options), config_default = {}, htmlfiles; // local html file array fs.readdir(path.resolve('src/'), function(err, arr) { if (err) {console.log(err)} htmlfiles = _.filter(arr, function(item) { return item.lastIndexOf('.html') !== -1; }); htmlfiles = _.map(htmlfiles, function(item) { return item.substring(0, item.lastIndexOf('.html')); }); // cover config_default by config_cover _.each(htmlfiles, function(item) { config_default[item] = {}; config_default[item]['path_to_layout'] = (config_cover[item] && config_cover[item]['path_to_layout']) || 'src/' + item + '.html'; config_default[item]['path_to_data'] = (config_cover[item] && config_cover[item]['path_to_data']) || ["src/data/global.json"]; }); Object.keys(config_default).forEach(function(key) { if (reqFileName === key + options.file_extension) { var fileData = ejs_static.get_data(key, config_default); var layoutData = ejs_static.get_layout(key, config_default, options); renderedFile = ejs_static.render_file(layoutData, fileData, _.extend({}, _)); } }); // set the correct media-type, default 'text/plain' var type = accepts(req).types('text/plain', 'text/html', 'application/json', 'text/css', 'application/javascript', 'application/x-javascript', 'application/x-font-woff'); switch(type){ case 'text/css': res.setHeader('Content-Type', 'text/css'); break; case 'application/javascript': res.setHeader('Content-Type', 'application/javascript'); break; case 'application/x-javascript': res.setHeader('Content-Type', 'application/x-javascript'); break; case 'text/plain': req.url.lastIndexOf('.js')!==-1 && res.setHeader('Content-Type', 'application/javascript'); req.url.lastIndexOf('.css')!==-1 && res.setHeader('Content-Type', 'text/css'); req.url.lastIndexOf('.woff')!==-1 && res.setHeader('Content-Type', 'application/x-font-woff'); break; } res.end(renderedFile || file); }); }); }; grunt.initConfig({ // 全局变量 banner: '/*! Project: '+pkg.name+'\n * Version: '+pkg.version+'\n * Date: <%= grunt.template.today("yyyy-mm-dd hh:MM:ss TT") %>\n * Author: '+pkg.author.name+'\n */', connect: { site_src: { options: { hostname: ipAddress, port: 9000, base: ['src/'], livereload: true, open: true, //打开默认浏览器 middleware: [ function(req, res, next){ return renderTpl(req,res,next); }, middleware_directory(path.resolve('src/')) ] } }, site_dest: { options: { hostname: ipAddress, port: 9001, base: ['dest/'], livereload: true, keepalive: true, //保持sever不退出 open: true //打开默认浏览器 } } }, cssmin: { options: { banner: '<%= banner %>' }, minify: { expand: true, cwd: 'dest/css', src: ['*.css', '!*.min.css'], dest: 'dest/css', ext: '.css' } }, uglify: { options: { banner: '<%= banner %>', mangle: true }, dist: { files: [{ expand: true, cwd: 'dest/js', src: '**/*.js', dest: 'dest/js' }] } }, clean: { build: ["dest"], release: ["dest/slice", "dest/data", "dest/partial"], zip: ["assets"], svn: [".tmp_svn"] }, copy: { release: { expand: true, cwd: 'src/', src: ['**', '!sass', '!sass/{,*/}*', '!css/*.map', '!img/psd','!img/psd/{,*/}*'], dest: 'dest/' }, zip_dest: { expand: true, cwd: 'dest/', src: ['js/{,*/}*', 'img/{,*/}*', 'css/*'], dest: 'assets/dest' }, zip_src: { expand: true, cwd: 'src/', src: ['**', '!sass', '!sass/{,*/}*', '!css/*.map', '!img/psd','!img/psd/{,*/}*'], dest: 'assets/src' } }, autoprefixer: { options: { browsers: ['> 1%', 'last 2 versions', 'ff 17', 'opera 12.1', 'ie 8'] }, dist: { expand: true, flatten: true, src: 'src/css/*.css', dest: 'src/css/' } }, watch: { css: { files: ['src/sass/{,*/}*.scss'], tasks:['sass','autoprefixer'] }, livereload: { options: { livereload: true }, files: ['src/*.html', 'src/css/*.css', 'src/js/*.js', 'src/partial/*.ejs', 'src/data/*.json'] } }, imagemin: { options: { pngquant: true }, dist: { files: [{ expand: true, cwd: 'dest/img/', src: ['**/*.{png,jpg,jpeg}'], // 优化 img 目录下所有 png/jpg/jpeg 图片 dest: 'dest/img/' // 优化后的图片保存位置,覆盖旧图片,并且不作提示 }] } }, adisprite: { all: { srcCss: 'dest/css', srcImg: 'dest/slice', destCss: 'dest/css', destImg: 'dest/img/sprite', 'padding': 5, 'algorithm': 'binary-tree', 'engine': 'gm', 'exportOpts': { 'format': 'png', 'quality': 90 } } }, sass: { dist: { options: { outputStyle: 'expanded', //nested, compact, compressed, expanded sourceComments: 'map', sourceMap: true }, files: [{ expand: true, cwd: 'src/sass', src: ['*.scss','!_*.scss','!*/_*.scss'], dest: 'src/css', ext: '.css' }] } }, ejs_static: { release:{ options: { dest: 'dest/', path_to_data: 'src/data/config.json', path_to_layouts: 'src/', underscores_to_dashes: false, file_extension: '.html', underscore: true } } }, concat: { trans_html: { options: { process: function(src, filepath) { var regex = /((href|src)=['"][\s]*)(?!http[s]?\:|\#|\/)([\?\#\=\/\w._-]*)([\s]*['"])/g; return src.replace(regex, '$1'+ASSETS_URL+'$3$4'); } }, files: [{ expand: true, cwd: 'dest/', src: '*.html', dest: 'assets/dest/' }] } }, compress: { zip: { options:{ archive: 'assets.zip' }, files: [{ expand: true, cwd: 'assets/', src: '**' }] } }, push_svn: { options: { message: '初始化项目:' + pkg.name, username: 'liujianxin', password: 'g2551', trymkdir: true }, assets: { src: 'dest', dest: 'http://svn.duowan.com:9999/svn/web/program/assets/' + proj_namespace, tmp: '.tmp_svn' } } }); // 默认任务 grunt.registerTask('default', ['connect:site_src', 'watch']); // 自定义端口 grunt.task.registerTask('port', 'multi port', function(arg) { if(arguments.length === 0){ console.log('端口号不能为空!') }else{ grunt.config.set('connect.port'+arg,{ options: { hostname: ipAddress, port: arg, base: ['src/'], livereload: +arg+1, open: true, middleware: [ function(req, res, next){ return renderTpl(req,res,next); }, middleware_directory(path.resolve('src/')) ] } }); grunt.config.set('watch.livereload',{ options: { livereload: +arg+1 }, files: ['src/*.html', 'src/css/*.css', 'src/js/*.js'] }) grunt.task.run(['connect:port'+arg, 'watch']); } }); // webserver 查看发布目录 grunt.registerTask('dest', ['connect:site_dest']); // 发布任务 grunt.registerTask('release', ['sass', 'autoprefixer', 'clean:build', 'copy:release', 'cssmin', 'uglify', 'imagemin', 'ejs_static:release', 'clean:release', 'connect:site_dest']); // 提交dest到静态文件svn grunt.task.registerTask('assets', 'commit message', function(arg) { grunt.config.merge({ push_svn:{ options: { message: arg, pushIgnore: ['*.html', '.DS_Store', '.idea/**', '.tmp_svn/**', '.svn/**'] } } }) grunt.task.run(['push_svn:assets', 'clean:svn']); }); // release后,zip打包 grunt.registerTask('zip',['copy:zip_src', 'copy:zip_dest', 'concat:trans_html', 'compress', 'clean:zip']); };