Gruntfile.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371
  1. module.exports = function(grunt) {
  2. 'use strict';
  3. require('load-grunt-tasks')(grunt);
  4. var path = require('path');
  5. var fs = require('fs');
  6. var pkg = require('./package');
  7. var proj_namespace = path.join(pkg.description, pkg.name, pkg.version, '/');
  8. var ASSETS_URL = 'http://assets.dwstatic.com/'+ proj_namespace;
  9. var ipAddress = require('network-address')();
  10. var get_files = require('grunt-adiejs-static/lib/get_files').get_files,
  11. get_data = require('grunt-adiejs-static/lib/data').get_data,
  12. get_layout = require('grunt-adiejs-static/lib/layout').get_layout,
  13. render_file = require('grunt-adiejs-static/lib/render').render_file,
  14. write_file = require('grunt-adiejs-static/lib/write_file').write_file,
  15. get_helper_functions = require('grunt-adiejs-static/lib/get_helper_functions').get_helper_functions,
  16. ejs_static = require('grunt-adiejs-static/lib/ejs_static'),
  17. _ = require('grunt-adiejs-static/node_modules/underscore'),
  18. middleware_directory = require('grunt-contrib-connect/node_modules/connect/lib/middleware/directory'),
  19. accepts = require('grunt-contrib-connect/node_modules/connect/node_modules/serve-index/node_modules/accepts'),
  20. url = require('url');
  21. var renderTpl = function (req, res, next){
  22. var reqPathName = decodeURIComponent(url.parse(req.originalUrl).pathname),
  23. reqFileName = reqPathName.substring(reqPathName.lastIndexOf('/')+1),
  24. localPathName = path.resolve('src/', reqPathName.substring(1)),
  25. renderedFile;
  26. fs.readFile(localPathName, function(err, file) {
  27. if (err) {return next();}
  28. var options = {
  29. path_to_data: 'src/data/config.json',
  30. file_extension: '.html',
  31. underscore: true
  32. },
  33. config_cover = ejs_static.get_files(options),
  34. config_default = {},
  35. htmlfiles; // local html file array
  36. fs.readdir(path.resolve('src/'), function(err, arr) {
  37. if (err) {console.log(err)}
  38. htmlfiles = _.filter(arr, function(item) {
  39. return item.lastIndexOf('.html') !== -1;
  40. });
  41. htmlfiles = _.map(htmlfiles, function(item) {
  42. return item.substring(0, item.lastIndexOf('.html'));
  43. });
  44. // cover config_default by config_cover
  45. _.each(htmlfiles, function(item) {
  46. config_default[item] = {};
  47. config_default[item]['path_to_layout'] = (config_cover[item] && config_cover[item]['path_to_layout']) || 'src/' + item + '.html';
  48. config_default[item]['path_to_data'] = (config_cover[item] && config_cover[item]['path_to_data']) || ["src/data/global.json"];
  49. });
  50. Object.keys(config_default).forEach(function(key) {
  51. if (reqFileName === key + options.file_extension) {
  52. var fileData = ejs_static.get_data(key, config_default);
  53. var layoutData = ejs_static.get_layout(key, config_default, options);
  54. renderedFile = ejs_static.render_file(layoutData, fileData, _.extend({}, _));
  55. }
  56. });
  57. // set the correct media-type, default 'text/plain'
  58. var type = accepts(req).types('text/plain', 'text/html', 'application/json', 'text/css', 'application/javascript', 'application/x-javascript', 'application/x-font-woff');
  59. switch(type){
  60. case 'text/css':
  61. res.setHeader('Content-Type', 'text/css');
  62. break;
  63. case 'application/javascript':
  64. res.setHeader('Content-Type', 'application/javascript');
  65. break;
  66. case 'application/x-javascript':
  67. res.setHeader('Content-Type', 'application/x-javascript');
  68. break;
  69. case 'text/plain':
  70. req.url.lastIndexOf('.js')!==-1 && res.setHeader('Content-Type', 'application/javascript');
  71. req.url.lastIndexOf('.css')!==-1 && res.setHeader('Content-Type', 'text/css');
  72. req.url.lastIndexOf('.woff')!==-1 && res.setHeader('Content-Type', 'application/x-font-woff');
  73. break;
  74. }
  75. res.end(renderedFile || file);
  76. });
  77. });
  78. };
  79. grunt.initConfig({
  80. // 全局变量
  81. 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 */',
  82. connect: {
  83. site_src: {
  84. options: {
  85. hostname: ipAddress,
  86. port: 9000,
  87. base: ['src/'],
  88. livereload: true,
  89. open: true, //打开默认浏览器
  90. middleware: [
  91. function(req, res, next){
  92. return renderTpl(req,res,next);
  93. },
  94. middleware_directory(path.resolve('src/'))
  95. ]
  96. }
  97. },
  98. site_dest: {
  99. options: {
  100. hostname: ipAddress,
  101. port: 9001,
  102. base: ['dest/'],
  103. livereload: true,
  104. keepalive: true, //保持sever不退出
  105. open: true //打开默认浏览器
  106. }
  107. }
  108. },
  109. cssmin: {
  110. options: {
  111. banner: '<%= banner %>'
  112. },
  113. minify: {
  114. expand: true,
  115. cwd: 'dest/css',
  116. src: ['*.css', '!*.min.css'],
  117. dest: 'dest/css',
  118. ext: '.css'
  119. }
  120. },
  121. uglify: {
  122. options: {
  123. banner: '<%= banner %>',
  124. mangle: true
  125. },
  126. dist: {
  127. files: [{
  128. expand: true,
  129. cwd: 'dest/js',
  130. src: '**/*.js',
  131. dest: 'dest/js'
  132. }]
  133. }
  134. },
  135. clean: {
  136. build: ["dest"],
  137. release: ["dest/slice", "dest/data", "dest/partial"],
  138. zip: ["assets"],
  139. svn: [".tmp_svn"]
  140. },
  141. copy: {
  142. release: {
  143. expand: true,
  144. cwd: 'src/',
  145. src: ['**', '!sass', '!sass/{,*/}*', '!css/*.map', '!img/psd','!img/psd/{,*/}*'],
  146. dest: 'dest/'
  147. },
  148. zip_dest: {
  149. expand: true,
  150. cwd: 'dest/',
  151. src: ['js/{,*/}*', 'img/{,*/}*', 'css/*'],
  152. dest: 'assets/dest'
  153. },
  154. zip_src: {
  155. expand: true,
  156. cwd: 'src/',
  157. src: ['**', '!sass', '!sass/{,*/}*', '!css/*.map', '!img/psd','!img/psd/{,*/}*'],
  158. dest: 'assets/src'
  159. }
  160. },
  161. autoprefixer: {
  162. options: {
  163. browsers: ['> 1%', 'last 2 versions', 'ff 17', 'opera 12.1', 'ie 8']
  164. },
  165. dist: {
  166. expand: true,
  167. flatten: true,
  168. src: 'src/css/*.css',
  169. dest: 'src/css/'
  170. }
  171. },
  172. watch: {
  173. css: {
  174. files: ['src/sass/{,*/}*.scss'],
  175. tasks:['sass','autoprefixer']
  176. },
  177. livereload: {
  178. options: {
  179. livereload: true
  180. },
  181. files: ['src/*.html', 'src/css/*.css', 'src/js/*.js', 'src/partial/*.ejs', 'src/data/*.json']
  182. }
  183. },
  184. imagemin: {
  185. options: {
  186. pngquant: true
  187. },
  188. dist: {
  189. files: [{
  190. expand: true,
  191. cwd: 'dest/img/',
  192. src: ['**/*.{png,jpg,jpeg}'], // 优化 img 目录下所有 png/jpg/jpeg 图片
  193. dest: 'dest/img/' // 优化后的图片保存位置,覆盖旧图片,并且不作提示
  194. }]
  195. }
  196. },
  197. adisprite: {
  198. all: {
  199. srcCss: 'dest/css',
  200. srcImg: 'dest/slice',
  201. destCss: 'dest/css',
  202. destImg: 'dest/img/sprite',
  203. 'padding': 5,
  204. 'algorithm': 'binary-tree',
  205. 'engine': 'gm',
  206. 'exportOpts': {
  207. 'format': 'png',
  208. 'quality': 90
  209. }
  210. }
  211. },
  212. sass: {
  213. dist: {
  214. options: {
  215. outputStyle: 'expanded',
  216. sourceComments: 'map',
  217. sourceMap: true
  218. },
  219. files: [{
  220. expand: true,
  221. cwd: 'src/sass',
  222. src: ['*.scss','!_*.scss','!*/_*.scss'],
  223. dest: 'src/css',
  224. ext: '.css'
  225. }]
  226. }
  227. },
  228. ejs_static: {
  229. release:{
  230. options: {
  231. dest: 'dest/',
  232. path_to_data: 'src/data/config.json',
  233. path_to_layouts: 'src/',
  234. underscores_to_dashes: false,
  235. file_extension: '.html',
  236. underscore: true
  237. }
  238. }
  239. },
  240. concat: {
  241. trans_html: {
  242. options: {
  243. process: function(src, filepath) {
  244. var regex = /((href|src)=['"][\s]*)(?!http[s]?\:|\#|\/)([\?\#\=\/\w._-]*)([\s]*['"])/g;
  245. return src.replace(regex, '$1'+ASSETS_URL+'$3$4');
  246. }
  247. },
  248. files: [{
  249. expand: true,
  250. cwd: 'dest/',
  251. src: '*.html',
  252. dest: 'assets/dest/'
  253. }]
  254. }
  255. },
  256. compress: {
  257. zip: {
  258. options:{
  259. archive: 'assets.zip'
  260. },
  261. files: [{
  262. expand: true,
  263. cwd: 'assets/',
  264. src: '**'
  265. }]
  266. }
  267. },
  268. push_svn: {
  269. options: {
  270. message: '初始化项目:' + pkg.name,
  271. username: 'liujianxin',
  272. password: 'g2551',
  273. trymkdir: true
  274. },
  275. work: {
  276. src: './',
  277. dest: 'http://svn.duowan.com:9999/svn/design/work/' + __dirname.substring(__dirname.indexOf('/work/')+5),
  278. tmp: '.tmp_svn'
  279. },
  280. assets: {
  281. src: 'dest',
  282. dest: 'http://svn.duowan.com:9999/svn/web/program/assets/' + proj_namespace,
  283. tmp: '.tmp_svn'
  284. }
  285. }
  286. });
  287. // 默认任务
  288. grunt.registerTask('default', ['connect:site_src', 'watch']);
  289. // 自定义端口
  290. grunt.task.registerTask('port', 'multi port', function(arg) {
  291. if(arguments.length === 0){
  292. console.log('端口号不能为空!')
  293. }else{
  294. grunt.config.set('connect.port'+arg,{
  295. options: {
  296. hostname: ipAddress,
  297. port: arg,
  298. base: ['src/'],
  299. livereload: +arg+1,
  300. open: true,
  301. middleware: [
  302. function(req, res, next){
  303. return renderTpl(req,res,next);
  304. },
  305. middleware_directory(path.resolve('src/'))
  306. ]
  307. }
  308. });
  309. grunt.config.set('watch.livereload',{
  310. options: {
  311. livereload: +arg+1
  312. },
  313. files: ['src/*.html', 'src/css/*.css', 'src/js/*.js']
  314. })
  315. grunt.task.run(['connect:port'+arg, 'watch']);
  316. }
  317. });
  318. // webserver 查看发布目录
  319. grunt.registerTask('dest', ['connect:site_dest']);
  320. // 发布任务
  321. grunt.registerTask('release', ['sass', 'autoprefixer', 'clean:build', 'copy:release', 'adisprite', 'cssmin', 'uglify', 'imagemin', 'ejs_static:release', 'clean:release', 'connect:site_dest']);
  322. // release后,zip打包
  323. grunt.registerTask('zip',['copy:zip_src', 'copy:zip_dest', 'concat:trans_html', 'compress', 'clean:zip']);
  324. // 提交src和配置文件到工作svn
  325. grunt.task.registerTask('work', 'commit message', function(arg) {
  326. grunt.config.merge({
  327. push_svn:{
  328. options: {
  329. message: arg,
  330. pushIgnore: ['dest/**', '.DS_Store', '.idea/**', 'node_modules/**', '.tmp_svn/**', '.svn/**'],
  331. remove: true
  332. }
  333. }
  334. })
  335. grunt.task.run(['push_svn:work', 'clean:svn']);
  336. });
  337. // 提交dest到静态文件svn
  338. grunt.task.registerTask('assets', 'commit message', function(arg) {
  339. grunt.config.merge({
  340. push_svn:{
  341. options: {
  342. message: arg,
  343. pushIgnore: ['*.html', '.DS_Store', '.idea/**', '.tmp_svn/**', '.svn/**']
  344. }
  345. }
  346. })
  347. grunt.task.run(['push_svn:assets', 'clean:svn']);
  348. });
  349. };