UNPKG

generator-clam

Version:
560 lines (509 loc) 12.8 kB
var path = require('path'), fs = require('fs-extra'), os = require('os'), exec = require('child_process').exec; module.exports = function (grunt) { var file = grunt.file; var task = grunt.task; var pathname = path.basename(__dirname); var files = doWalk('./'); // files.js 存储项目中的所有js文件 // file.css 存储项目中的所有css文件 // file.less 存储项目中所有less文件 // file.pic 存储项目中所有图片文件 // ======================= 配置每个任务 ========================== grunt.initConfig({ pkg: grunt.file.readJSON('abc.json'), // 配置默认分支 currentBranch: 'master', // 对页面进行清理 clean: { build: { src: 'build/*' } }, /** * 将src目录中的KISSY文件做编译打包,仅做合并,源文件需要指定名称 * KISSY.add('package/path/to/file',function(S){}); * * @link https://github.com/daxingplay/grunt-kmc * * 如果需要只生成依赖关系表,不做合并 * 在kmc.options中增加两个参数: * depFilePath: 'build/mods.js', * comboOnly: true, * comboMap: true, */ kmc: { options: { packages: [ { name: '<%= pkg.name %>', path: '../', charset: 'utf-8' } ], map: [/*['<%= pkg.name %>/', '<%= pkg.name %>/<%= currentBranch %>/']*/] }, main: { files: [ { // 这里指定项目根目录下所有文件为入口文件,自定义入口请自行添加 expand: true, cwd: './', src: [ '**/*.js', '!build/**/*', '!Gruntfile.js', '!node_modules/**/*', '!doc/**/*'], dest: 'build/' } ] } // 若有新任务,请自行添加 /* "simple-example": { files: [ { src: "a.js", dest: "build/index.js" } ] } */ }, // 将css文件中引用的本地图片上传CDN并替换url mytps: { options: { argv: "--inplace" }, all: [ '**/*.css', '!build/**/*', '!node_modules/**/*', '!doc/**/*'] }, // CSS-Combo: 合并项目中所有css,通过@import "other.css" 来处理CSS的依赖关系 css_combo: { options: { paths: './' }, main: { files: [ { expand: true, cwd: 'build', src: ['**/*.css'], dest: 'build/', ext: '.css' } ] } }, // FlexCombo服务配置: https://npmjs.org/package/grunt-flexcombo flexcombo: { options: { target: '', urls: '/<%= pkg.group %>/<%= pkg.name %>', port: '<%= pkg.port %>', servlet: '?', separator: ',', charset: 'utf8', proxy: { // 代理配置 interface: { // 接口 mock 配置 hosts: [/*'api.m.taobao.com', 'api.waptest.taobao.com', 'api.test.taobao.com'*/], // 接口 mock 要代理的主机名 script: 'proxy/interface.js' // 接口 mock 的执行脚本路径 }, webpage: { urls: [/*/taobao\.com/*/], // 页面代理需要代理的 url 模式(字符串/正则表达式) script: 'proxy/webpage.js' // 页面代理执行脚本路径 } } }, main: {} }, // 编译LESS为CSS https://github.com/gruntjs/grunt-contrib-less less: { options: { strictImports: true, relativeUrls: true // 将从其他 less 文件中导入的 url() 中相对路径图片引用替换为相对当前 less 文件路径 }, main: { files: [ { expand: true, cwd: 'build/', src: ['**/*.less'], dest: 'build/', ext: '.css' } ] } }, // 压缩JS https://github.com/gruntjs/grunt-contrib-uglify uglify: { options: { banner: '/*! <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd HH:MM:ss") %> */\n', beautify: { ascii_only: true } }, main: { files: [ { expand: true, cwd: 'build/', src: ['**/*.js', '!**/*-min.js'], dest: 'build/', ext: '-min.js' } ] } }, // 压缩CSS https://github.com/gruntjs/grunt-contrib-cssmin cssmin: { main: { files: [ { expand: true, cwd: 'build/', src: ['**/*.css', '!**/*-min.css'], dest: 'build/', ext: '-min.css' } ] } }, // 监听JS、CSS、LESS文件的修改 watch: { 'js': { files: [ '**/*.js' , '!**/*-min.js', '!build/**/*', '!**/node_modules/**/*'], tasks: [ 'kmc', 'uglify' ] }, 'css': { files: [ '**/*.css' , '!**/*-min.css', '!build/**/*', '!**/node_modules/**/*'], tasks: [ 'copy', 'cssmin' ] }, 'less': { files: [ '**/*.less', '!**/*-min.less', '!build/**/*', '!**/node_modules/**/*'], tasks: ['less', 'cssmin'] } }, // 发布命令 exec: { tag: { command: 'git tag publish/<%= currentBranch %>' }, publish: { command: 'git push origin publish/<%= currentBranch %>:publish/<%= currentBranch %>' }, commit: { command: function (msg) { var command = 'git commit -m "' + grunt.config.get('currentBranch') + ' - ' + grunt.template.today("yyyy-mm-dd HH:MM:ss") + ' ' + msg + '"'; return command; } }, add: { command: 'git add .' }, sync: { command: 'git stash;git pull origin daily/<%= currentBranch %>;git stash pop;' }, prepub: { command: 'git push origin daily/<%= currentBranch %>:daily/<%= currentBranch %>' }, grunt_publish: { command: 'grunt default:publish' }, grunt_prepub: { command: function (msg) { return 'grunt default:prepub:' + msg; } }, new_branch: { command: 'git checkout -b daily/<%= currentBranch %>' } }, // 拷贝文件 copy: { main: { files: [ { src: files.pic, dest: 'build/', filter: 'isFile' }, { src: files.js, dest: 'build/', filter: 'isFile' }, { src: files.css, dest: 'build/', filter: 'isFile' }, { src: files.less, dest: 'build/', filter: 'isFile' } ] } }, // 替换config中的版本号@@version replace: { dist: { options: { variables: { 'version': '<%= pkg.version %>' } }, files: [ { expand: true, cwd: 'build/', dest: 'build/', src: ['**/*'] } ] } }, // juicer 模板编译为 kissy 模块,文档:https://www.npmjs.org/package/grunt-tpl-compiler tpl_compiler: { options: { ext: '-tpl' }, main: { files: [ { expand: true, cwd: 'src/', src: ['**/*.tpl.html'], dest: 'src/' } ] } } // 合并文件 /* concat: { dist: { src: ['from.css'], dest: 'build/to.css' } }, */ // YUIDoc: 对build目录中的js文件生成文档,放入doc/中 /* yuidoc: { compile: { name: 'generator-clam', description: 'A Clam generator for Yeoman', options: { paths: 'build/', outdir: 'doc/' } } } */ }); // ======================= 载入使用到的通过NPM安装的模块 ========================== grunt.loadNpmTasks('grunt-contrib-clean'); grunt.loadNpmTasks('grunt-contrib-uglify'); grunt.loadNpmTasks('grunt-css-combo'); grunt.loadNpmTasks('grunt-contrib-less'); grunt.loadNpmTasks('grunt-contrib-cssmin'); grunt.loadNpmTasks('grunt-contrib-watch'); grunt.loadNpmTasks('grunt-kmc'); grunt.loadNpmTasks('grunt-exec'); grunt.loadNpmTasks('grunt-contrib-copy'); grunt.loadNpmTasks('grunt-mytps'); grunt.loadNpmTasks('grunt-flexcombo'); grunt.loadNpmTasks('grunt-replace'); // 根据需要打开这些配置 //grunt.loadNpmTasks('grunt-kissy-template'); //grunt.loadNpmTasks('grunt-contrib-connect'); //grunt.loadNpmTasks('grunt-contrib-concat'); //grunt.loadNpmTasks('grunt-contrib-yuidoc'); // ======================= 注册Grunt 各个操作 ========================== /** * 正式发布 */ grunt.registerTask('publish', 'clam publish...', function () { task.run('exec:grunt_publish'); }); grunt.registerTask('pub', 'clam publish...', function () { task.run('exec:grunt_publish'); }); /** * 预发布 */ grunt.registerTask('prepub', 'clam pre publish...', function () { task.run('exec:grunt_prepub'); }); /** * 监听修改 */ grunt.registerTask('listen', 'clam watch ...', function () { task.run('watch'); }); /** * 启动服务 */ grunt.registerTask('server', 'clam server...', function () { task.run(['flexcombo', 'watch']); }); /* * 获取当前库的信息 **/ grunt.registerTask('info', 'clam info...', function () { var abcJSON = {}; try { abcJSON = require(path.resolve(process.cwd(), 'abc.json')); console.log('\n' + abcJSON.repository.url); } catch (e) { console.log('未找到abc.json'); } }); /* * 获取当前最大版本号,并创建新分支 **/ grunt.registerTask('newbranch', 'clam newBranch...', function (type) { var done = this.async(); exec('git branch -a & git tag', function (err, stdout, stderr, cb) { var versions = stdout.match(/\d+\.\d+\.\d+/ig), r = clamUtil.getBiggestVersion(versions); if (!r || !versions) { r = '0.1.0'; } else { r[2]++; r = r.join('.'); } grunt.log.write(('新分支:daily/' + r).green); grunt.config.set('currentBranch', r); task.run(['exec:new_branch']); // 回写人 abc.json 的 version try { abcJSON = require(path.resolve(process.cwd(), 'abc.json')); abcJSON.version = r; fs.writeJSONFile("abc.json", abcJSON, function (err) { if (err) { console.log(err); } else { console.log("update abc.json."); } }); } catch (e) { console.log('未找到abc.json'); } done(); }); }); // ======================= 注册Grunt主流程 ========================== return grunt.registerTask('default', 'clam running ...', function (type, msg) { var done = this.async(); // 获取当前分支 exec('git branch', function (err, stdout, stderr, cb) { var reg = /\*\s+daily\/(\S+)/, match = stdout.match(reg); if (!match) { grunt.log.error('当前分支为 master 或者名字不合法(daily/x.y.z),请切换到daily分支'.red); grunt.log.error('创建新daily分支:grunt newbranch'.yellow); return; } grunt.log.write(('当前分支:' + match[1]).green); grunt.config.set('currentBranch', match[1]); done(); }); // 构建和发布任务 if (!type) { task.run(['clean:build', 'tpl_compiler', 'copy', 'less', 'mytps', 'css_combo', 'kmc', 'replace', 'uglify', 'cssmin'/*'concat','yuidoc'*/]); } else if ('publish' === type || 'pub' === type) { task.run(['exec:tag', 'exec:publish']); } else if ('prepub' === type) { task.run(['exec:add', 'exec:commit:' + msg]); task.run(['exec:prepub']); } }); // ======================= 辅助函数 ========================== // 遍历当前目录的文件 function walk(uri, files) { var stat = fs.lstatSync(uri); if (stat.isFile() && !/(build|node_modules|demo|doc|\.git|\.+)[\\|\/]/i.test(uri) && !/grunt.+/i.test(uri)) { switch (path.extname(uri)) { case '.css': files.css.push(uri); break; case '.js': files.js.push(uri); break; case '.less': files.less.push(uri); break; case '.scss': files.scss.push(uri); break; case '.png': files.pic.push(uri); break; case '.gif': files.pic.push(uri); break; case '.jpg': files.pic.push(uri); break; default: files.other.push(uri); } } if (stat.isDirectory()) { fs.readdirSync(uri).forEach(function (part) { walk(path.join(uri, part), files); }); } } // 得到文件结构的数据结构 function doWalk(rootDir) { var files = { css: [], less: [], scss: [], js: [], other: [] // 暂时没用 }; walk(rootDir, files); return files; } function getBiggestVersion(A) { var a = []; var b = []; var t = []; var r = []; if (!A) { return [0, 0, 0]; } for (var i = 0; i < A.length; i++) { if (A[i].match(/^\d+\.\d+\.\d+$/)) { var sp = A[i].split('.'); a.push([ Number(sp[0]), Number(sp[1]), Number(sp[2]) ]); } } var r = findMax(findMax(findMax(a, 0), 1), 2); return r[0]; } // a:二维数组,index,比较第几个 // return:返回保留比较后的结果组成的二维数组 function findMax(a, index) { var t = []; var b = []; var r = []; for (var i = 0; i < a.length; i++) { t.push(Number(a[i][index])); } var max = Math.max.apply(this, t); for (var i = 0; i < a.length; i++) { if (a[i][index] === max) { b.push(i); } } for (var i = 0; i < b.length; i++) { r.push(a[b[i]]); } return r; } };