├── .gitignore ├── src ├── structure │ ├── src │ │ ├── images │ │ │ └── a.png │ │ ├── lib │ │ │ └── vendors.js │ │ ├── css │ │ │ └── index.css │ │ ├── js │ │ │ └── index.js │ │ └── html │ │ │ └── index.html │ ├── .gitignore │ ├── .babelrc │ ├── package.json │ └── readme.md ├── utils │ ├── gulpFileCount.js │ ├── isIgnore.js │ ├── network.js │ ├── isUrl.js │ └── logger.js ├── .gitignore ├── functions │ ├── handleImage.js │ ├── watch.js │ ├── server.js │ ├── handleCount.js │ ├── handleCSS.js │ └── handleJS.js ├── webpack.dll.example.js ├── config.example.js ├── webpack.config.example.js └── index.js ├── index.js ├── package.json ├── bin └── html-bundler.js └── readme.md /.gitignore: -------------------------------------------------------------------------------- 1 | demo/ 2 | node_modules/ -------------------------------------------------------------------------------- /src/structure/src/images/a.png: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/structure/src/lib/vendors.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | require('./bin/html-bundler.js'); 2 | -------------------------------------------------------------------------------- /src/structure/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | dev/ 3 | .happypack/ -------------------------------------------------------------------------------- /src/structure/src/css/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | color: red; 3 | } -------------------------------------------------------------------------------- /src/structure/src/js/index.js: -------------------------------------------------------------------------------- 1 | console.log('a'); 2 | alert('hello world'); -------------------------------------------------------------------------------- /src/utils/gulpFileCount.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | filecount: 0 3 | } 4 | -------------------------------------------------------------------------------- /src/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | dev/ 3 | !.gitignore 4 | *~ 5 | *.zip 6 | .happy/ 7 | .happypack/ 8 | .git/ 9 | .idea/ -------------------------------------------------------------------------------- /src/structure/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | ["transform-runtime", { 4 | "helpers": false, 5 | "polyfill": true, 6 | "regenerator": true, 7 | "moduleName": "babel-runtime" 8 | }] 9 | ], 10 | "presets": [ 11 | ["env", { 12 | "modules": false, 13 | "debug": false, 14 | "targets": { 15 | "browsers": ["> 1%", "last 2 versions", "ie <= 8"] 16 | } 17 | }], 18 | ] 19 | } -------------------------------------------------------------------------------- /src/functions/handleImage.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var gulpif = require('gulp-if'); 3 | var path = require('path'); 4 | 5 | var handleImage = function(imgArr, conf, filename, env) { 6 | //暂不处理 7 | // var stream = gulp.src(imgArr) 8 | // if (conf.custom && conf.custom.imgs && conf.custom.imgs.length) { 9 | // conf.custom.imgs.forEach(function (task) { 10 | // stream = stream.pipe(task.func(task.opts)); 11 | // }); 12 | // } 13 | } 14 | 15 | module.exports = handleImage; -------------------------------------------------------------------------------- /src/utils/isIgnore.js: -------------------------------------------------------------------------------- 1 | var currentPath = process.cwd(); 2 | var path = require('path'); 3 | 4 | module.exports = function(absolutePath, ignoreArr) { 5 | if (!ignoreArr) { 6 | var ignoreArr = []; 7 | } 8 | 9 | checkArr = []; 10 | 11 | for (var i = 0; i < ignoreArr.length; i++) { 12 | checkArr.push(path.join(currentPath, ignoreArr[i])); 13 | } 14 | 15 | for (var i = 0; i < checkArr.length; i++) { 16 | if (absolutePath.indexOf(checkArr[i]) > -1) { 17 | return true 18 | } 19 | } 20 | 21 | return false; 22 | } -------------------------------------------------------------------------------- /src/utils/network.js: -------------------------------------------------------------------------------- 1 | var os = require('os'); 2 | 3 | var network = { 4 | port: '8008', 5 | getIPAddress: function() { 6 |   var interfaces = os.networkInterfaces(); 7 |   for (var devName in interfaces) { 8 |     var iface = interfaces[devName]; 9 |       for (var i = 0; i < iface.length; i++) { 10 |         var alias = iface[i]; 11 |         if (alias.family === 'IPv4' && alias.address !== '127.0.0.1' && !alias.internal) { 12 |           return alias.address; 13 |         } 14 |       } 15 |   } 16 | }, 17 | } 18 | 19 | module.exports = network; 20 | -------------------------------------------------------------------------------- /src/structure/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "html-bundler-project", 3 | "version": "0.1.5", 4 | "description": "", 5 | "scripts": { 6 | "dll": "node webpack.dll.js" 7 | }, 8 | "dependencies": { 9 | 10 | }, 11 | "devDependencies": { 12 | "webpack": "^3.6.0", 13 | "babel-loader": "^7.1.2", 14 | "babel-core": "^6.26.0", 15 | "babel-preset-env": "^1.6.0", 16 | "babel-plugin-transform-runtime": "^6.23.0", 17 | "url-loader": "^0.5.7", 18 | "html-loader": "^0.4.4", 19 | "json-loader": "^0.5.4", 20 | "file-loader": "^0.9.0", 21 | "happypack": "^4.0.0" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/utils/isUrl.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @desc 3 | * is.js中的is.url对//www.baidu.com这种继承协议不支持,因此需要修改一个独立实现。 4 | */ 5 | module.exports = function (str) { 6 | var reg = /^(?:(?:https?|ftp):)?(\/\/)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})))(?::\d{2,5})?(?:\/\S*)?$/i 7 | if (typeof str === 'string') { 8 | return reg.test(str); 9 | } 10 | else { 11 | return false; 12 | } 13 | } -------------------------------------------------------------------------------- /src/utils/logger.js: -------------------------------------------------------------------------------- 1 | var colors = require('colors'); 2 | colors.setTheme({ 3 | error: ['red', 'bold'], 4 | warn: ['yellow', 'bold'], 5 | info: ['green', 'bold'], 6 | log: ['white'], 7 | 8 | }); 9 | 10 | module.exports = { 11 | error: function(msg) { 12 | console.error(msg.error); 13 | }, 14 | 15 | info: function(msg) { 16 | console.info(msg.info); 17 | }, 18 | 19 | warn: function(msg) { 20 | console.warn(msg.warn); 21 | }, 22 | 23 | log: function(msg) { 24 | console.log(msg); 25 | }, 26 | 27 | notice: function(msg, type) { 28 | var type = type || 'info'; 29 | this[type]('***********' + msg + '***********'); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/structure/src/html/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Hello World! 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |

Hello World!

15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/functions/watch.js: -------------------------------------------------------------------------------- 1 | var logger = require('../utils/logger.js'); 2 | var gulp = require('gulp'); 3 | /* 4 | * 分类进行监听,防止出现一个文件变化整个项目编译的情况 5 | */ 6 | 7 | module.exports = function(run, conf) { 8 | if (conf.watchFolder) { 9 | 10 | gulp.watch(conf.watchFolder.js, function() { 11 | logger.info('Your Javascript files have changed!!') 12 | run('js'); 13 | }); 14 | 15 | gulp.watch(conf.watchFolder.css, function() { 16 | logger.info('Your style files have changed!!') 17 | run('css'); 18 | }); 19 | 20 | gulp.watch(conf.watchFolder.image, function() { 21 | logger.info('Your Image files have changed!!') 22 | run('img'); 23 | }); 24 | 25 | gulp.watch(conf.watchFolder.html, function() { 26 | logger.info('Your HTML files have changed!!') 27 | run('any'); 28 | }); 29 | } 30 | } -------------------------------------------------------------------------------- /src/functions/server.js: -------------------------------------------------------------------------------- 1 | var logger = require('../utils/logger'); 2 | var network = require('../utils/network'); 3 | var bird = require('gulp-bird'); 4 | var connect = require('gulp-connect'); 5 | var is = require('is_js'); 6 | var path = require('path'); 7 | var currentPath = process.cwd(); 8 | 9 | module.exports = function(usage, config, port) { 10 | 11 | var port = port || '8008'; 12 | network.port = port; 13 | 14 | if (usage === 'bird' && is.object(config.birdConfig)) { 15 | var birdconf = config.birdConfig; 16 | var server = {}; 17 | var TranspondRules = {}; 18 | server[port] = { 19 | basePath: path.join(currentPath, birdconf.basePath) 20 | }; 21 | TranspondRules[port] = { 22 | targetServer: birdconf.targetServer, 23 | ajaxOnly: birdconf.ajaxOnly 24 | }; 25 | bird.start(server, TranspondRules, birdconf.toolsConf); 26 | logger.info('Bird server runing at port: ' + port + '.'); 27 | } 28 | else if (usage && is.object(config.serverConfig)) { 29 | config.serverConfig.port = port; 30 | connect.server(config.serverConfig); 31 | logger.info('connect server runing at port: ' + port + '.'); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/webpack.dll.example.js: -------------------------------------------------------------------------------- 1 | const webpack = require('webpack'); 2 | const path = require('path'); 3 | 4 | const vendors = []; 5 | 6 | const options = { 7 | output: { 8 | path: path.join(__dirname, 'src/lib'), 9 | filename: 'vendors.js', 10 | library: 'vendors', 11 | }, 12 | entry: { 13 | vendor: vendors, 14 | }, 15 | plugins: [ 16 | new webpack.DllPlugin({ 17 | path: 'manifest.json', 18 | name: 'vendors', 19 | context: __dirname, 20 | }), 21 | new webpack.optimize.UglifyJsPlugin() 22 | ], 23 | } 24 | 25 | // webpack(webpackConf).run(function(err, stats) { 26 | webpack(options, function (err, stats) { 27 | // spinner.stop() 28 | if (err) throw err 29 | process.stdout.write(stats.toString({ 30 | // context: path.join(__dirname, '../lib'), 31 | colors: true, 32 | cached: false, 33 | modules: true, 34 | children: false, 35 | chunks: false, 36 | chunkModules: false 37 | }) + '\n\n') 38 | 39 | if (stats.hasErrors()) { 40 | console.log(' Build failed with errors.\n') 41 | process.exit(1) 42 | } 43 | 44 | console.log(' Build complete.\n') 45 | console.log( 46 | ' Tip: built files are meant to be served over an HTTP server.\n' + 47 | ' Opening index.html over file:// won\'t work.\n' 48 | ); 49 | }); 50 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "html-bundler", 3 | "version": "0.7.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "bin": { 10 | "hb": "./bin/html-bundler.js" 11 | }, 12 | "author": "xieyu33333", 13 | "license": "ISC", 14 | "dependencies": { 15 | "autoprefixer": "6.7.0", 16 | "cheerio": "^0.22.0", 17 | "colors": "1.1.2", 18 | "commander": "2.9.0", 19 | "del": "2.2.2", 20 | "file-loader": "0.9.0", 21 | "fs-extra": "^1.0.0", 22 | "gulp": "^3.9.1", 23 | "gulp-base64": "0.1.3", 24 | "gulp-bird": "0.2.4", 25 | "gulp-changed": "1.3.2", 26 | "gulp-clean-css": "2.3.2", 27 | "gulp-concat": "^2.6.1", 28 | "gulp-connect": "5.0.0", 29 | "gulp-debug": "3.0.0", 30 | "gulp-file-count": "1.0.3", 31 | "gulp-if": "^2.0.2", 32 | "gulp-inline-source": "^3.0.0", 33 | "gulp-less": "^3.3.0", 34 | "gulp-minify-html": "^1.0.6", 35 | "gulp-plumber": "1.1.0", 36 | "gulp-postcss": "6.3.0", 37 | "gulp-sourcemaps": "^1.9.1", 38 | "gulp-uglify": "^2.0.0", 39 | "gulp-util": "^3.0.8", 40 | "happypack": "4.0.0", 41 | "html-loader": "0.4.4", 42 | "is_js": "0.9.0", 43 | "json-loader": "0.5.4", 44 | "lodash": "^4.17.4", 45 | "node-sloc": "^0.1.10", 46 | "through2": "2.0.3", 47 | "url-loader": "0.5.7", 48 | "webpack": "^3.6.0", 49 | "webpack-stream": "^4.0.0", 50 | "is-stream": "^1.1.0" 51 | }, 52 | "devDependencies": {} 53 | } 54 | -------------------------------------------------------------------------------- /src/functions/handleCount.js: -------------------------------------------------------------------------------- 1 | const sloc = require('node-sloc'); 2 | 3 | module.exports = function() { 4 | const options = { 5 | path: './', // Required. The path to walk or file to read. 6 | extensions: ['tag', 'jsx', 'less', 'sass'], // Additional file extensions to look for. Required if ignoreDefault is set to true. 7 | ignorePaths: ['node_modules', '.git', '.happypack', 'dev', 'dist'], // A list of directories to ignore. 8 | ignoreDefault: false, // Whether to ignore the default file extensions or not 9 | logger: false, // Optional. Outputs extra information to if specified. 10 | } 11 | 12 | // Using promises 13 | const prettyPrint = (obj) => { 14 | const str = 15 | ` 16 | +---------------------------------------------------+ 17 | | SLOC | ${obj.sloc.sloc} \t\t| 18 | |-------------------------------|-------------------- 19 | | Lines of comments | ${obj.sloc.comments} \t\t| 20 | |-------------------------------|-------------------- 21 | | Blank lines | ${obj.sloc.blank} \t\t| 22 | |-------------------------------|-------------------- 23 | | Files counted | ${obj.sloc.files} \t\t| 24 | |-------------------------------|-------------------- 25 | | Total LOC | ${obj.sloc.loc} \t\t| 26 | +---------------------------------------------------+ 27 | ` 28 | return str 29 | } 30 | 31 | sloc(options).then((res) => { 32 | console.log("\x1b[32m" + prettyPrint(res)) 33 | }); 34 | } 35 | 36 | -------------------------------------------------------------------------------- /src/functions/handleCSS.js: -------------------------------------------------------------------------------- 1 | var concat = require('gulp-concat'); 2 | var less = require('gulp-less'); 3 | var sourcemap = require('gulp-sourcemaps'); 4 | var cleancss = require('gulp-clean-css'); 5 | var changed = require('gulp-changed'); 6 | var gulp = require('gulp'); 7 | var gulpif = require('gulp-if'); 8 | var path = require('path'); 9 | var cwd = process.cwd(); 10 | var debug = require('gulp-debug'); 11 | var plumber = require('gulp-plumber'); 12 | var postcss = require('gulp-postcss'); 13 | var autoprefixer = require('autoprefixer'); 14 | 15 | 16 | var handleCSS = function(cssArr, conf, filename, env) { 17 | if (!conf.buildTarget.css && !conf.bundle && !conf.concat){ 18 | var target = function(file){ 19 | return path.dirname(path.join(conf.output, path.relative(conf.src, file.path))); 20 | } 21 | } 22 | else { 23 | var target = path.join(conf.output, conf.buildTarget.css); 24 | } 25 | 26 | var stream = gulp.src(cssArr) 27 | // .pipe(gulpif(env !== 'dest', changed(target, {extension: '.css'}))) 28 | .pipe(plumber()) 29 | .pipe(gulpif(conf.sourcemap, sourcemap.init())) 30 | .pipe(gulpif(true, debug({title: 'style file build:'}))) 31 | .pipe(gulpif(conf.less, less())); 32 | 33 | if (conf.custom && conf.custom.css && conf.custom.css.length) { 34 | conf.custom.css.forEach(function (task) { 35 | stream = stream.pipe(task.func(task.opts)); 36 | }); 37 | } 38 | 39 | stream = stream.pipe(gulpif(conf.concat, concat(filename + '.css'))) 40 | .pipe(postcss([ autoprefixer({ browsers: ['last 5 versions'] }) ])) 41 | .pipe(gulpif(conf.minify, cleancss())) 42 | // .pipe(gulpif(conf.base64, base64(conf.base64))) 43 | .pipe(gulpif(conf.sourcemap, sourcemap.write())) 44 | .pipe(gulp.dest(target)) 45 | 46 | } 47 | 48 | module.exports = handleCSS; -------------------------------------------------------------------------------- /bin/html-bundler.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | var commander = require('commander'); 3 | var htmlBundler = require('../src/index.js'); 4 | var logger = require('../src/utils/logger.js'); 5 | var process = require('process'); 6 | var fs = require('fs-extra'); 7 | var path = require('path'); 8 | var version = JSON.parse(fs.readFileSync(path.join(__dirname, '../package.json'), 'utf8')).version; 9 | var currentPath = process.cwd(); 10 | 11 | const DEFAULT_CONFIG = path.join(__dirname, '../src/config.example.js'); 12 | const REAL_CONFIG = path.join(currentPath, './html-bundler.config.js'); 13 | const DEFAULT_WEBPACK_CONFIG = path.join(__dirname, '../src/webpack.config.example.js'); 14 | const REAL_WEBPACK_CONFIG = path.join(currentPath, './webpack.config.js'); 15 | const DEFAULT_DLL_CONFIG = path.join(__dirname, '../src/webpack.dll.example.js'); 16 | const REAL_DLL_CONFIG = path.join(currentPath, './webpack.dll.js'); 17 | const DEFAULT_STRUCTURE = path.join(__dirname, '../src/structure'); 18 | 19 | 20 | /* 21 | * 如果不存在则创建html-bundler.config.js和webpack.config.js, 根据自己项目进行修改 22 | */ 23 | commander.version(version) 24 | 25 | 26 | commander.command('init') 27 | .description('if your project rootpath has not `html-bundler.config.js` & `webpack.config.js`, this command will create these files') 28 | .option('-w --webpack') 29 | .action(function(webpack) { 30 | if (!fs.existsSync(REAL_CONFIG)) { 31 | fs.copySync(DEFAULT_CONFIG, REAL_CONFIG); 32 | logger.info('html-bundler配置文件创建成功, 请根据项目情况进行配置'); 33 | } 34 | if (!fs.existsSync(REAL_WEBPACK_CONFIG) && webpack.webpack) { 35 | fs.copySync(DEFAULT_WEBPACK_CONFIG, REAL_WEBPACK_CONFIG); 36 | fs.copySync(DEFAULT_DLL_CONFIG, path.join(currentPath, './webpack.dll.js')); 37 | logger.info('webpack配置文件创建成功, 请根据项目情况进行修改并安装依赖'); 38 | } 39 | 40 | }) 41 | /* 42 | * 直接创建一个测试项目 43 | */ 44 | commander.command('create [project]') 45 | .description('create a empty project') 46 | .option('-w --webpack') 47 | .action(function(project, webpack) { 48 | fs.copySync(DEFAULT_STRUCTURE, path.join(currentPath, project)); 49 | fs.copySync(DEFAULT_CONFIG, path.join(currentPath, project, './html-bundler.config.js')); 50 | logger.notice('项目' + project + '创建成功'); 51 | if (webpack.webpack) { 52 | fs.copySync(DEFAULT_WEBPACK_CONFIG, path.join(currentPath, project, './webpack.config.js')); 53 | fs.copySync(DEFAULT_DLL_CONFIG, path.join(currentPath, project, './webpack.dll.js')); 54 | logger.info('webpack配置文件创建成功, 请根据项目情况进行修改并安装依赖'); 55 | } 56 | }) 57 | 58 | /* 59 | * 开发环境 60 | */ 61 | commander.command('dev') 62 | .description('dev') 63 | .option('-p --port ') 64 | .action(function(port) { 65 | htmlBundler('dev', port.port); 66 | }) 67 | 68 | /* 69 | * 生产环境打包 70 | */ 71 | commander.command('dest') 72 | .description('dest') 73 | .action(function() { 74 | htmlBundler('dest'); 75 | }) 76 | 77 | /* 78 | * QA环境打包 79 | */ 80 | commander.command('qa') 81 | .description('qa') 82 | .action(function() { 83 | htmlBundler('qa'); 84 | }) 85 | 86 | /* 87 | * RD环境打包 88 | */ 89 | commander.command('rd') 90 | .description('rd') 91 | .action(function() { 92 | htmlBundler('rd'); 93 | }) 94 | 95 | commander.parse(process.argv); -------------------------------------------------------------------------------- /src/config.example.js: -------------------------------------------------------------------------------- 1 | var destMod = { 2 | output: './dist/dest', 3 | minify: true, 4 | minifyHTML: true, 5 | bundle: true, 6 | concat: true, 7 | less: true, 8 | inline: false, 9 | codeCount: true, //代码统计 10 | sourcemap: false, 11 | watchFolder: null, 12 | custom: { 13 | js: [], 14 | css: [], 15 | imgs: [], 16 | html: [] 17 | },//自定义任务 18 | define: { 19 | __DEST__: true, 20 | __DEV__: false, 21 | __RD__: false, 22 | __QA__: false 23 | }, 24 | server: false, 25 | buildTarget: { 26 | js: './js/', 27 | css: './css/', 28 | imgs: './images/', 29 | html: './html/' 30 | }, 31 | }; 32 | 33 | var rdMod = Object.assign({}, destMod); 34 | var qaMod = Object.assign({}, destMod); 35 | 36 | rdMod.output = './dist/rd'; 37 | qaMod.output = './dist/qa'; 38 | rdMod.define = { 39 | __DEST__: false, 40 | __DEV__: false, 41 | __RD__: true, 42 | __QA__: false 43 | }; 44 | qaMod.define = { 45 | __DEST__: false, 46 | __DEV__: false, 47 | __RD__: false, 48 | __QA__: true 49 | }; 50 | 51 | /* eslint-disable */ 52 | module.exports = { 53 | src: './src', 54 | 55 | entries: ['./src/html/**', './src/*.html'], 56 | 57 | ignore: ['./src/lib'], 58 | 59 | imgFolder: './src/images', 60 | 61 | moveList: ['./src/lib'], //需要平移的目录和文件 62 | 63 | devMod: { 64 | entries: ['./src/html/**', './src/*.html'], //每种模式下内部的entries会覆盖外部的 65 | output: './dev', 66 | minify: false, 67 | minifyHTML: false, 68 | bundle: true, 69 | concat: false, 70 | sourcemap: true, 71 | less: true, 72 | inline: false, 73 | codeCount: false, //代码统计 74 | watchFolder: { 75 | css: ['./src/css'], 76 | js: ['./src/js'], 77 | imgs: ['./src/images'], 78 | html: ['./src/html'] 79 | }, 80 | define: { 81 | __DEST__: false, 82 | __DEV__: true, 83 | __RD__: false, 84 | __QA__: false 85 | }, //webpack环境变量定义,非webpack模式不生效 86 | custom: { 87 | js: [], 88 | css: [], 89 | imgs: [], 90 | html: [] 91 | },//自定义任务, 格式样例[{func: sass, opts: {logger: true}}, {func: task, opts: null }] 92 | server: true, 93 | buildTarget: 'default' 94 | }, 95 | 96 | destMod: destMod, 97 | 98 | rdMod: rdMod, 99 | 100 | qaMod: qaMod, 101 | 102 | birdConfig: { 103 | basePath: "./dev", 104 | targetServer: { 105 | port: "8276", 106 | host: "your server host", 107 |             headers: { 108 |               cookie: "" 109 | } 110 |         }, 111 | ajaxOnly: false, 112 | toolsConf: { 113 | weinre: { 114 | open: false, //和移动调试工具条中的vconsole冲突, 当为true时vconsole自动关闭 115 | port: 9001 116 | }, 117 | 118 | showTools: true //移动端调试工具条,PC端开发可关闭 119 | } 120 | }, 121 | 122 | serverConfig: { 123 | root: './dev' 124 | } 125 | } 126 | 127 | /* 128 | * buildTarget用于设置dist后的目录结构,如果选择default,则默认为css, js, html 129 | * 如果是一个对象,则表示自定义,不过目前只支持按照文件类型进行分类。 130 | */ 131 | 132 | 133 | -------------------------------------------------------------------------------- /src/webpack.config.example.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | var webpack = require('webpack'); 3 | var HappyPack = require('happypack'); 4 | var happyThreadPool = HappyPack.ThreadPool({ size: 4 }); 5 | 6 | var commonConf = { 7 | module: { 8 | //各种加载器,即让各种文件格式可用require引用 9 | loaders: [ 10 | { 11 | test: /\.tag$/, 12 | exclude: /node_modules/, 13 | loader: ['babel-loader', 'riotjs-loader'] 14 | }, 15 | { 16 | test: /\.js$/, 17 | exclude: /node_modules/, 18 | loaders: ['happypack/loader?id=js'] 19 | }, 20 | { 21 | test: /\.(jpeg|jpg|png|gif)$/, 22 | loader: 'url-loader?limit=10240' 23 | }, 24 | { 25 | test: /\.html$/, 26 | loader: 'html-loader' 27 | }, 28 | { 29 | test: /\.json$/, loader: 'json-loader' 30 | }, 31 | { 32 | test: /\.woff(\?.+)?$/, loader: "url?limit=10000&mimetype=application/font-woff" 33 | }, 34 | { 35 | test: /\.woff2(\?.+)?$/, loader: "url?limit=10000&mimetype=application/font-woff" 36 | }, 37 | { 38 | test: /\.ttf(\?.+)?$/, loader: "url?limit=10000&mimetype=application/octet-stream" 39 | }, 40 | { 41 | test: /\.eot(\?.+)?$/, loader: "file" 42 | }, 43 | { 44 | test: /\.svg(\?.+)?$/, loader: "url?limit=10000&mimetype=image/svg+xml" 45 | } 46 | ] 47 | }, 48 | resolve: { 49 | //配置别名,在项目中可缩减引用路径 50 | // alias: { 51 | // jquery: srcDir + "/js/lib/jquery.min.js", 52 | // core: srcDir + "/js/core", 53 | // ui: srcDir + "/js/ui" 54 | // } 55 | } 56 | }; 57 | 58 | var webpackConf = { 59 | dev: { 60 | devtool: "inline-source-map", //生成sourcemap,便于开发调试 61 | //devtool: "cheap-eval-source-map", //快速打包 62 | cache: true, 63 | plugins: [ 64 | new HappyPack({ 65 | id: 'js', 66 | cache: true, 67 | verbose: false, 68 | threadPool: happyThreadPool, 69 | loaders: [ 'babel-loader' ] 70 | }), 71 | ], 72 | module: commonConf.module, 73 | resolve: commonConf.resolve 74 | 75 | }, 76 | 77 | dest: { 78 | devtool: false, 79 | cache: false, 80 | plugins: [ 81 | new webpack.optimize.ModuleConcatenationPlugin(), 82 | new HappyPack({ 83 | id: 'js', 84 | cache: true, 85 | verbose: false, 86 | threadPool: happyThreadPool, 87 | loaders: [ 'babel-loader' ] 88 | }), 89 | ], 90 | module: commonConf.module, 91 | resolve: commonConf.resolve 92 | } 93 | }; 94 | 95 | try { 96 | var dllref = new webpack.DllReferencePlugin({ 97 | context: __dirname, 98 | manifest: require('./manifest.json'), 99 | }); 100 | webpackConf.dev.plugins.unshift(dllref); 101 | webpackConf.dest.plugins.unshift(dllref); 102 | } 103 | catch(e) { 104 | console.log('没有生成webpack DllReferencePlugin插件所需的 manifest.json'); 105 | } 106 | 107 | //一般来说,RD环境和QA环境打包配置和dest是一致的,但是需要不同的环境变量配置一些参数 108 | webpackConf.rd = Object.assign(webpackConf.dest, {}); 109 | webpackConf.qa = Object.assign(webpackConf.dest, {}); 110 | 111 | module.exports = webpackConf; -------------------------------------------------------------------------------- /src/structure/readme.md: -------------------------------------------------------------------------------- 1 | ## 使用手册 2 | 3 | ### Globle Mod 4 | 适用于一些较简单的项目 5 | 6 | ```sh 7 | npm install html-bundler -g 8 | ``` 9 | 10 | ```sh 11 | hb create project 12 | hb create project -w //add webpack.config.js local 13 | ``` 14 | 15 | 16 | ```sh 17 | cd project && npm install 18 | ``` 19 | 20 | 21 | ```sh 22 | hb dev -p 8080 23 | ``` 24 | 25 | 26 | ```sh 27 | hb dest 28 | ``` 29 | 30 | ### Local Mod 31 | 适用于正式项目 32 | 33 | ```sh 34 | cd your-project 35 | npm install html-bundler --save-dev 36 | ``` 37 | 38 | 自动生成html-bundler.config.js 39 | 40 | ```sh 41 | npm install html-bundler -g 42 | hb init 43 | hb init -w //自动生成webpack.config.js 44 | ``` 45 | 46 | create a js file (e.g: bundle.js)and write: 47 | 48 | ```js 49 | require('html-bundler') 50 | 51 | ``` 52 | 53 | ```sh 54 | node bundle.js dev -p 8080 55 | ``` 56 | 57 | ```sh 58 | node bundle.js dest 59 | ``` 60 | 61 | ### DLL优化 62 | 需要在webpack.dll.js的vendors中配置业务需要静态化的包,然后执行 63 | 64 | ```sh 65 | node webpack.dll.js 66 | 67 | ``` 68 | 执行完成后会生成一个manifest.json文件,每次修改vendors配置后需要重新生成。 69 | 70 | ### 配置文件解析 71 | ```js 72 | /* eslint-disable */ 73 | module.exports = { 74 | src: './src', //源代码所在路径 75 | 76 | entries: ['./src/html/**', './src/*.html'], //html入口文件 77 | 78 | ignore: ['./src/js/lib', './src/css/lib'], //不进行任何处理的路径 79 | 80 | imgFolder: './src/imgs', //图片目录 81 | 82 | moveList: ['./src/fonts', './src/a.js'], //需要平移的目录和文件 83 | 84 | devMod: { //开发模式 85 | output: './dev', //开发模式下打包后的输出位置 86 | minify: false, //是否最小化,如果开启,则js、css都会进行压缩 87 | minifyHTML: false, //是否压缩html,如果开启,则会对html文件进行压缩 88 | bundle: true, //是否使用webpack进行打包 89 | concat: false, //是否合并文件 90 | sourcemap: true, //是否进行sourcemap 91 | less: true, //是否进行less预处理 92 | inline: false, //是否把所有资源打成inline(目前不能和bundle配合使用) 93 | watchFolder: { //文件分类进行监听,这样修改js不会编译css,提高性能 94 | css: ['./src/css'], 95 | js: ['./src/js'], 96 | imgs: ['./src/imgs'], 97 | html: ['./src/html'] 98 | }, 99 | custom: { //自定义任务, 格式样例[{func: sass, opts: {logger: true}}, {func: task, opts: null }] 100 | js: [], 101 | css: [], 102 | imgs: [], 103 | html: [] 104 | }, 105 | server: true, //是否开启server,默认集成gulp-connect,如果配置为'bird',则使用bird。 106 | buildTarget: 'default' //buildTarget用于设置dist后的目录结构,如果选择default,则默认为css, js, html,如果是一个对象,则表示自定义,不过目前只支持按照文件类型进行分类。 107 | }, 108 | 109 | destMod: { //生产模式,配置项和开发模式完全相同 110 | output: './dist', 111 | minify: true, 112 | minifyHTML: true, 113 | bundle: true, 114 | concat: true, 115 | less: true, 116 | inline: false, 117 | sourcemap: false, 118 | watchFolder: null, 119 | custom: { 120 | js: [], 121 | css: [], 122 | imgs: [], 123 | html: [] 124 | }, 125 | server: false, 126 | buildTarget: { 127 | js: './js/', 128 | css: './css/', 129 | imgs: './images/', 130 | html: './html/' 131 | }, 132 | }, 133 | 134 | rdMod: { 135 | //rd环境配置项,内容同上 136 | }, 137 | 138 | qaMod: { 139 | //qa环境配置项,内容同上 140 | }, 141 | 142 | birdConfig: { //bird 配置项 143 | basePath: "./dev", 144 | targetServer: { 145 | port: "8276", 146 | host: "your server host", 147 |             headers: { 148 |               cookie: "" 149 | } 150 |         }, 151 | ajaxOnly: false 152 | }, 153 | 154 | serverConfig: { //gulp connect 配置项 155 | root: './dev' 156 | } 157 | } 158 | 159 | ``` 160 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # html-bundler 2 | 3 | ### 面临的业务困境 4 | 5 | ### Why html-bundler 6 | 目前最流行的打包工具是webpack和gulp,都有着良好的生态和海量的用户,但是在我们使用的过程中发现了以下一些问题: 7 | 8 | - webpack的上手难度较高,配置和学习成本都很高 9 | - 所有资源都用webpack打包的情况下性能较差,比如改行样式等10秒这种非常常见 10 | - gulp虽然上手简单,但是插件众多,如何进行选择和最佳实践是一个大问题 11 | - 每做一个项目都需要修改构建程序. 12 | 13 | html-bundler的特点: 14 | 15 | - html-bundler以html为入口文件,自动寻找引入的资源文件进行构建处理,这对绝大多数项目是通用的. 16 | - 通过一个简单易理解的配置文件使得构建流程可配置。 17 | - 一些简单的项目可以直接只做简单的less => css转换,production模式进行压缩合并以及版本号处理即可 18 | - 复杂的项目则引入webpack对js进行模块化的处理,并暴露接口让用户自己修改webpack配置文件 19 | - 集成了一些优化和日志工具,提升构建的性能和可维护性 20 | 21 | 22 | 23 | ### Globle Mod 24 | 适用于一些只需要简单压缩合并的活动项目,或者一些demo页 25 | 26 | ```sh 27 | npm install html-bundler -g 28 | ``` 29 | 30 | ```sh 31 | hb create project 32 | hb create project -w //add webpack.config.js local 33 | ``` 34 | 35 | 36 | ```sh 37 | cd project && npm install 38 | ``` 39 | 40 | 41 | ```sh 42 | hb dev -p 8080 43 | ``` 44 | 45 | 46 | ```sh 47 | hb dest 48 | ``` 49 | 50 | ### Local Mod 51 | 适用于正式项目 52 | 53 | ```sh 54 | cd your-project 55 | npm install html-bundler --save-dev 56 | ``` 57 | 58 | 自动生成html-bundler.config.js 59 | 60 | ```sh 61 | npm install html-bundler -g 62 | hb init 63 | hb init -w //自动生成webpack.config.js 64 | ``` 65 | 66 | create a js file (e.g: bundle.js)and write: 67 | 68 | ```js 69 | require('html-bundler') 70 | 71 | ``` 72 | 73 | ```sh 74 | node bundle.js dev -p 8080 75 | ``` 76 | 77 | ```sh 78 | node bundle.js dest 79 | ``` 80 | 81 | ### 配置文件解析 82 | ```js 83 | /* eslint-disable */ 84 | module.exports = { 85 | src: './src', //源代码所在路径 86 | 87 | entries: ['./src/html/**', './src/*.html'], //html入口文件 88 | 89 | ignore: ['./src/js/lib', './src/css/lib'], //不进行任何处理的路径 90 | 91 | imgFolder: './src/imgs', //图片目录 92 | 93 | moveList: ['./src/fonts', './src/a.js'], //需要平移的目录和文件 94 | 95 | devMod: { //开发模式 96 | output: './dev', //开发模式下打包后的输出位置 97 | minify: false, //是否最小化,如果开启,则js、css都会进行压缩 98 | minifyHTML: false, //是否压缩html,如果开启,则会对html文件进行压缩 99 | bundle: true, //是否使用webpack进行打包 100 | concat: false, //是否合并文件 101 | sourcemap: true, //是否进行sourcemap 102 | less: true, //是否进行less预处理 103 | inline: false, //是否把所有资源打成inline(目前不能和bundle配合使用) 104 | watchFolder: { //文件分类进行监听,这样修改js不会编译css,提高性能 105 | css: ['./src/css'], 106 | js: ['./src/js'], 107 | imgs: ['./src/imgs'], 108 | html: ['./src/html'] 109 | }, 110 | custom: { //自定义任务, 格式样例[{func: sass, opts: {logger: true}}, {func: task, opts: null }] 111 | js: [], 112 | css: [], 113 | imgs: [], 114 | html: [] 115 | }, 116 | server: true, //是否开启server,默认集成gulp-connect,如果配置为'bird',则使用bird。 117 | buildTarget: 'default' //buildTarget用于设置dist后的目录结构,如果选择default,则默认为css, js, html,如果是一个对象,则表示自定义,不过目前只支持按照文件类型进行分类。 118 | }, 119 | 120 | destMod: { //生产模式,配置项和开发模式完全相同 121 | output: './dist', 122 | minify: true, 123 | minifyHTML: true, 124 | bundle: true, 125 | concat: true, 126 | less: true, 127 | inline: false, 128 | sourcemap: false, 129 | watchFolder: null, 130 | custom: { 131 | js: [], 132 | css: [], 133 | imgs: [], 134 | html: [] 135 | }, 136 | server: false, 137 | buildTarget: { 138 | js: './js/', 139 | css: './css/', 140 | imgs: './images/', 141 | html: './html/' 142 | }, 143 | }, 144 | 145 | rdMod: { 146 | //rd环境配置项,内容同上 147 | }, 148 | 149 | qaMod: { 150 | //qa环境配置项,内容同上 151 | }, 152 | 153 | birdConfig: { //bird 配置项 154 | basePath: "./dev", 155 | targetServer: { 156 | port: "8276", 157 | host: "your server host", 158 |             headers: { 159 |               cookie: "" 160 | } 161 |         }, 162 | ajaxOnly: false 163 | }, 164 | 165 | serverConfig: { //gulp connect 配置项 166 | root: './dev' 167 | } 168 | } 169 | 170 | ``` 171 | 172 | ### DLL优化 173 | 如果你使用webpack模块化开发模式,一些基础依赖库如果也每次构建,会大大拖慢构建的速度,因此我们引入了webpack的dll插件。 174 | 175 | ##### step1 176 | 修改webpack.dll.js文件中的vendors数组,将依赖放进数组 177 | 178 | ```js 179 | const vendors = []; 180 | ``` 181 | 182 | ##### step2 183 | 执行 184 | 185 | ```sh 186 | npm run dll 187 | ``` 188 | 生成`manifest.json`文件和`src/lib/vendors.js`文件 189 | 190 | ##### step3 191 | 192 | 在html文件中引入`../lib/vendors.js`文件,放在其他js文件之前 193 | 194 | 195 | 196 | -------------------------------------------------------------------------------- /src/functions/handleJS.js: -------------------------------------------------------------------------------- 1 | var concat = require('gulp-concat'); 2 | var uglify = require('gulp-uglify'); 3 | var sourcemap = require('gulp-sourcemaps'); 4 | var webpack = require('webpack-stream'); 5 | var originWebpack = require('webpack'); 6 | var changed = require('gulp-changed'); 7 | var gulp = require('gulp'); 8 | var gulpif = require('gulp-if'); 9 | var path = require('path'); 10 | var is = require('is_js'); 11 | var currentPath = process.cwd(); 12 | var debug = require('gulp-debug'); 13 | var fs = require('fs'); 14 | var logger = require('../utils/logger'); 15 | var network = require('../utils/network'); 16 | var gulpFileCount = require('../utils/gulpFileCount'); 17 | var _ = require('lodash'); 18 | var gutil = require('gulp-util'); 19 | 20 | var htmlCount = 0; 21 | 22 | var handleJS = function(jsArr, conf, filename, env, wpconfig) { 23 | if (conf.bundle) { 24 | if (wpconfig) { 25 | var webpackConfig = wpconfig; 26 | } 27 | else { 28 | if (!fs.existsSync(path.join(currentPath, './webpack.config.js'))) { 29 | var webpackConfig = require('../webpack.config.example.js'); 30 | } 31 | else { 32 | var webpackConfig = require(path.join(currentPath, './webpack.config')); 33 | } 34 | } 35 | 36 | var generateWebpackConf = function(webpackConfig, filename, env) { 37 | var originConf = webpackConfig[env]; 38 | var hasDefinePlugin; 39 | if (!is.object(originConf)) { 40 | return 41 | } 42 | else { 43 | if (originConf.output) { 44 | originConf.output = _.assign({}, originConf.output); 45 | originConf.output.filename = filename + ".js"; 46 | } 47 | else { 48 | originConf.output = {filename: filename + ".js"}; 49 | } 50 | } 51 | 52 | for (var i = 0; i < originConf.plugins.length; i++) { 53 | if (originConf.plugins[i] instanceof originWebpack.DefinePlugin) { 54 | hasDefinePlugin = true; 55 | } 56 | } 57 | 58 | //definePlugin规则:webpack.conf中的声明 > hb.conf中的声明 > 默认规则 59 | if (!hasDefinePlugin) { 60 | if (!conf.define || (typeof conf.define !== 'object')) { 61 | var defineObj = {'process.env.NODE_ENV': JSON.stringify(env)}; 62 | defineObj['__' + env.toUpperCase() + '__'] = true; 63 | originConf.plugins.push( 64 | new originWebpack.DefinePlugin(defineObj) 65 | ); 66 | } 67 | else { 68 | var defineObj = Object.assign({'process.env.NODE_ENV': JSON.stringify(env)}, conf.define); 69 | originConf.plugins.push( 70 | new originWebpack.DefinePlugin(defineObj) 71 | ); 72 | } 73 | } 74 | return originConf; 75 | } 76 | } 77 | else { 78 | var generateWebpackConf = function() {}; 79 | } 80 | 81 | 82 | if (!conf.buildTarget.js && !conf.bundle && !conf.concat){ 83 | var target = function(file){ 84 | return path.dirname(path.join(conf.output, path.relative(conf.src, file.path))); 85 | } 86 | } 87 | else { 88 | var target = path.join(conf.output, conf.buildTarget.js); 89 | } 90 | var stream = gulp.src(jsArr) 91 | // .pipe(gulpif(env !== 'dest', changed(target, {extension: '.js'}))) 92 | .pipe(gulpif(true, debug({title: 'JS file build:'}))) 93 | .pipe(gulpif(conf.sourcemap, sourcemap.init())) 94 | 95 | if (conf.custom && conf.custom.css && conf.custom.css.length) { 96 | conf.custom.js.forEach(function (task) { 97 | stream = stream.pipe(task.func(task.opts)); 98 | }) 99 | } 100 | 101 | stream = stream.pipe(gulpif(conf.bundle, webpack(generateWebpackConf(webpackConfig, filename, env)))) 102 | .on('error', function(err) { 103 | // 这样语法错误不会打断watcher和server 104 | gutil.log('WEBPACK ERROR', gutil.colors.red(err.message)); 105 | this.emit('end'); 106 | }) 107 | .pipe(gulpif(conf.minify, uglify())) 108 | .pipe(gulpif(conf.concat, concat(filename + '.js'))) 109 | .pipe(gulpif(conf.sourcemap, sourcemap.write())) 110 | .pipe(gulp.dest(target)) 111 | .on('end', function(res) { 112 | //这是每个entry打包后end的时候,不是所有entry打包完成后,所以不能在这里输出end 113 | htmlCount ++; 114 | if(gulpFileCount.filecount && gulpFileCount.filecount === htmlCount) { 115 | htmlCount = 0; 116 | logger.notice('构建完成=^_^='); 117 | conf.server ? logger.info('Server Address: ' + 'http://' + network.getIPAddress() + ':' + network.port) : null; 118 | } 119 | }) 120 | } 121 | 122 | module.exports = handleJS; 123 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | Array.prototype.unique = function () { 2 | var res = []; 3 | var json = {}; 4 | for (var i = 0; i < this.length; i++) { 5 | if (!json[this[i]]) { 6 | res.push(this[i]); 7 | json[this[i]] = 1; 8 | } 9 | } 10 | return res; 11 | } 12 | 13 | module.exports = function(env, port, hbconfig, wpconfig) { 14 | var gulp = require('gulp'); 15 | var gulpif = require('gulp-if'); 16 | var inlinesource = require('gulp-inline-source'); 17 | var htmlmin = require('gulp-minify-html'); 18 | var base64 = require('gulp-base64'); 19 | var count = require('gulp-file-count'); 20 | var del = require('del'); 21 | 22 | 23 | var handleServer = require('./functions/server.js'); 24 | var handleWatch = require('./functions/watch.js'); 25 | var handleJS = require('./functions/handleJS.js'); 26 | var handleCSS = require('./functions/handleCSS.js'); 27 | var handleImage = require('./functions/handleImage.js'); 28 | var handleCount = require('./functions/handleCount.js'); 29 | 30 | var through = require('through2'); 31 | var cheerio = require('cheerio'); 32 | var path = require('path'); 33 | var fs = require('fs-extra'); 34 | var process = require('process'); 35 | var is = require('is_js'); 36 | 37 | var logger = require('./utils/logger.js'); 38 | var isIgnore = require('./utils/isIgnore.js'); 39 | var isUrl = require('./utils/isUrl.js'); 40 | var gulpFileCount = require('./utils/gulpFileCount.js'); 41 | 42 | var currentPath = process.cwd(); 43 | 44 | var getAbsolutePath = function(origin, extra) { 45 | if (!origin) { 46 | return; 47 | } 48 | if (!extra) { 49 | var extra = ''; 50 | } 51 | if (is.string(origin)) { 52 | origin = path.join(currentPath, origin, extra); 53 | } 54 | else if (is.array(origin)) { 55 | origin.forEach(function(item, i) { 56 | origin[i] = path.join(currentPath, item, extra); 57 | }) 58 | } 59 | } 60 | 61 | /* 62 | * 文件缺失容错处理 63 | */ 64 | // if (!fs.existsSync(path.join(currentPath, './html-bundler.config.js'))) { 65 | // logger.error('当前目录下缺少html-bundler.config.js 配置文件,请使用`hb init`或自己手动创建。'); 66 | // return 67 | // } 68 | 69 | try { 70 | var config = hbconfig || require(path.join(currentPath, './html-bundler.config')); 71 | } catch(e) { 72 | logger.error('html-bundler.config.js 配置文件出现错误'); 73 | return 74 | } 75 | 76 | if (!wpconfig && !fs.existsSync(path.join(currentPath, './webpack.config.js'))) { 77 | logger.info('当前目录下没有webpack.config.js文件 ,将使用默认配置,如果需要自定义,请使用`hb init -w`命令进行创建。'); 78 | } 79 | 80 | /* 81 | * config filter & judge env 82 | */ 83 | var conf = {}; 84 | 85 | var options = [ 86 | 'entries', 87 | 'output', 88 | 'minify', 89 | 'minifyHTML', 90 | 'bundle', 91 | 'sourcemap', 92 | 'concat', 93 | 'less', 94 | 'inline', 95 | 'server', 96 | 'custom', 97 | 'codeCount', 98 | 'watchFolder', 99 | 'imgFolder', 100 | 'define', 101 | 'buildTarget']; 102 | 103 | options.forEach(function(item) { 104 | conf[item] = config[env + 'Mod'][item]; 105 | }) 106 | 107 | /* 108 | * handle buildTarget 109 | */ 110 | var defaultBuildTarget = { 111 | js: './js/', 112 | css: './css/', 113 | imgs: './images/', 114 | html: './html/' 115 | }; 116 | 117 | if (!conf.buildTarget || conf.buildTarget === 'default') { 118 | conf.buildTarget = defaultBuildTarget; 119 | } 120 | else if (is.object(conf.buildTarget)) { 121 | conf.buildTarget.js = conf.buildTarget.js || './js/'; 122 | conf.buildTarget.css = conf.buildTarget.css || './css/'; 123 | conf.buildTarget.imgs = conf.buildTarget.imgs || './images/'; 124 | conf.buildTarget.html = conf.buildTarget.html || './html/'; 125 | } 126 | else if (conf.buildTarget === 'same') { 127 | conf.buildTarget = { 128 | js: '', 129 | css: '', 130 | imgs: '', 131 | html: '' 132 | } 133 | } 134 | 135 | 136 | conf.src = path.join(currentPath, config.src); 137 | conf.output = path.join(currentPath, conf.output); 138 | config.imgFolder && (conf.imgSrc = path.join(currentPath, config.imgFolder, './**')); 139 | 140 | if (conf.entries) { 141 | getAbsolutePath(conf.entries); 142 | } 143 | getAbsolutePath(config.entries); 144 | getAbsolutePath(config.moveList); 145 | 146 | 147 | /* 148 | * handle watchFolder 149 | */ 150 | if (is.object(conf.watchFolder)) { 151 | getAbsolutePath(conf.watchFolder.css, '**'); 152 | getAbsolutePath(conf.watchFolder.js, '**'); 153 | getAbsolutePath(conf.watchFolder.imgs, '**'); 154 | getAbsolutePath(conf.watchFolder.html, '**'); 155 | } 156 | 157 | /* 158 | * 给所有资源添加inline属性,供gulp-inline-source使用 159 | */ 160 | var addInlineAttr = function() { 161 | return through.obj(function (file, enc, cb) { 162 | if (file.isNull()) { 163 | this.push(file); 164 | return cb(); 165 | } 166 | 167 | if (file.isStream()) { 168 | return cb(); 169 | } 170 | 171 | var content = file.contents.toString(); 172 | var $ = cheerio.load(content, {xmlMode: false, decodeEntities: false}); 173 | 174 | $('script').each(function(i, item) { 175 | var src = $(item).attr('src'); 176 | if (!isUrl(src)) { 177 | $(item).attr('inline', 'inline'); 178 | } 179 | }); 180 | $('link').each(function(i, item) { 181 | var href = $(item).attr('href'); 182 | if (!isUrl(href)) { 183 | $(item).attr('inline', 'inline'); 184 | } 185 | }); 186 | $('img').attr('inline', 'inline'); 187 | 188 | file.contents = new Buffer($.html()); 189 | this.push(file); 190 | cb(); 191 | }); 192 | } 193 | 194 | /* 195 | * 主处理函数 196 | */ 197 | var findResource = function(handleLimit) { 198 | return through.obj(function (file, enc, cb) { 199 | if (file.isNull()) { 200 | this.push(file); 201 | return cb(); 202 | } 203 | 204 | if (file.isStream()) { 205 | return cb(); 206 | } 207 | 208 | var jsArr = []; 209 | var cssArr = []; 210 | var imgArr = []; 211 | var ignoreArr = []; 212 | var content = file.contents.toString(); 213 | var $ = cheerio.load(content); 214 | var filename = file.path.replace(file.base, '').replace('.html', ''); 215 | var cwd = file.cwd; 216 | 217 | /* 218 | * 将所有相对路径改为绝对路径,以适应不同目录结构 219 | */ 220 | var getPath = function(item, arr, attr) { 221 | var originPath = item.attr(attr); 222 | if (is.string(originPath) && !isUrl(originPath)) { 223 | var htmlPath = path.dirname(file.path); 224 | var result = path.join(htmlPath, originPath); 225 | if (!isIgnore(result, config.ignore)) { 226 | arr.push(result); 227 | } 228 | else { 229 | ignoreArr.push(result); 230 | } 231 | } 232 | } 233 | 234 | $('script').each(function(i, item) { 235 | getPath($(item), jsArr, 'src'); 236 | }) 237 | 238 | $('link').each(function(i, item) { 239 | getPath($(item), cssArr, 'href'); 240 | }) 241 | 242 | $('img').each(function(i, item) { 243 | getPath($(item), imgArr, 'src'); 244 | }); 245 | 246 | /* 247 | * 先去重以提升编译效率 248 | */ 249 | imgArr.unique(); 250 | cssArr.unique(); 251 | jsArr.unique(); 252 | ignoreArr.unique(); 253 | 254 | 255 | var replaceResource = function(type) { 256 | var htmlDir = path.relative(file.base, path.dirname(file.path)); 257 | var htmlOutput = path.join(conf.output, conf.buildTarget.html, htmlDir); 258 | var jsOutput = path.join(conf.output, conf.buildTarget.js); 259 | var cssOutput = path.join(conf.output, conf.buildTarget.css); 260 | var imgsOutput = path.join(conf.output, conf.buildTarget.imgs); 261 | var timeStamp = new Date().getTime(); 262 | var jsPath = path.join(path.relative(htmlOutput, jsOutput), filename +'.js') + '?v=' + timeStamp; 263 | var cssPath = path.join(path.relative(htmlOutput, cssOutput), filename +'.css') + '?v=' + timeStamp;; 264 | if (type !== 'css') { 265 | var JSAppended = false; 266 | $('script').each(function(i, item) { 267 | var src = $(item).attr('src'); 268 | var parent = $(item).parent(); 269 | $(item).remove(); 270 | if (src && !isUrl(src) && !isIgnore(path.join(file.base, src), config.ignore)) { 271 | if (!JSAppended) { 272 | parent.append(''); 273 | JSAppended = true; 274 | } 275 | } 276 | else { 277 | parent.append($(item)); 278 | } 279 | }); 280 | } 281 | 282 | if (type !== 'js') { 283 | var CSSAppended = false; 284 | $('link').each(function(i, item) { 285 | var href = $(item).attr('href'); 286 | var parent = $(item).parent(); 287 | $(item).remove(); 288 | if (href && !isUrl(href) && !isIgnore(path.join(file.base, href), config.ignore)) { 289 | if (!CSSAppended) { 290 | parent.append(''); 291 | CSSAppended = true 292 | } 293 | } 294 | else { 295 | parent.append($(item)); 296 | } 297 | }); 298 | } 299 | } 300 | 301 | var addVersion = function(type) { 302 | if (type !== 'css') { 303 | $('script').each(function(i, item) { 304 | var src = $(this).attr('src') 305 | if (src) { 306 | var origin = src.replace(/.\w+$/g, '.js'); 307 | $(this).attr('src', origin + '?v=' + new Date().getTime()); 308 | } 309 | 310 | }) 311 | } 312 | 313 | if (type !== 'js') { 314 | $('link').each(function(i, item) { 315 | var href = $(this).attr('href'); 316 | if (href) { 317 | var origin = href.replace(/.\w+$/g, '.css'); 318 | $(this).attr('href', origin + '?v=' + new Date().getTime()); 319 | } 320 | }) 321 | } 322 | } 323 | 324 | if (conf.concat || conf.bundle) { //如果bundle成一个文件 325 | replaceResource(); 326 | } 327 | else { 328 | addVersion(); 329 | } 330 | 331 | if (handleLimit === 'js') { 332 | handleJS(jsArr, conf, filename, env, wpconfig); 333 | } 334 | else if (handleLimit === 'css') { 335 | handleCSS(cssArr, conf, filename, env); 336 | } 337 | else if (handleLimit === 'imgs') { 338 | handleImage(imgArr, conf, filename, env); 339 | } 340 | else { 341 | handleJS(jsArr, conf, filename, env, wpconfig); 342 | handleCSS(cssArr, conf, filename, env); 343 | handleImage(imgArr, conf, filename, env); 344 | } 345 | 346 | /* 347 | * 被忽略的文件以不变的目录结构放到 348 | */ 349 | if (ignoreArr.length) { 350 | gulp.src(ignoreArr) 351 | .pipe(gulp.dest(function(file){ 352 | return path.dirname(path.join(conf.output, path.relative(conf.src, file.path))); 353 | })); 354 | } 355 | 356 | file.contents = new Buffer($.html({xmlMode: false, decodeEntities: false})); 357 | this.push(file); 358 | 359 | cb(); 360 | }); 361 | 362 | } 363 | 364 | var run = function(env) { 365 | logger.info('build begin!!'); 366 | var getTarget = function(type) { 367 | if (!conf.buildTarget[type]){ 368 | var target = function(file){ 369 | return path.dirname(path.join(conf.output, path.relative(conf.src, file.path))); 370 | } 371 | } 372 | else { 373 | var target = path.join(conf.output, conf.buildTarget[type]); 374 | } 375 | return target; 376 | } 377 | 378 | var htmlTarget = getTarget('html'); 379 | var imageTarget = getTarget('imgs'); 380 | 381 | var promise = new Promise((resolve, reject) => { 382 | if (env !== 'js' && env !== 'css') { 383 | if (conf.imgSrc) { 384 | logger.notice('执行图片复制'); 385 | gulp.src(conf.imgSrc) 386 | .on('error', reject) 387 | .pipe(gulp.dest(imageTarget)) 388 | .on('end', resolve) 389 | } 390 | } 391 | else { 392 | resolve(); 393 | } 394 | }); 395 | 396 | promise.then(function() { 397 | var entries = conf.entries || config.entries; 398 | var stream = gulp.src(entries) 399 | .pipe(count({ 400 | // message: '<%= files %>? That\'s ## too many!', 401 | getFileCount: function(entriesCount) { 402 | gulpFileCount.filecount = entriesCount; 403 | } 404 | })) 405 | .pipe(gulpif(!conf.inline, findResource(env))) 406 | 407 | if (conf.custom && conf.custom.html && conf.custom.html.length) { 408 | conf.custom.html.forEach(function (task) { 409 | stream = stream.pipe(task.func(task.opts)); 410 | }); 411 | } 412 | 413 | stream = stream.pipe(gulpif(conf.inline, addInlineAttr())) 414 | .pipe(gulpif(conf.inline, inlinesource())) 415 | .pipe(gulpif(conf.minifyHTML, htmlmin())) 416 | .on('error', function() { 417 | logger.notice('构建失败::>_<::'); 418 | }) 419 | .pipe(gulp.dest(htmlTarget)) 420 | .on('end', function() { 421 | if (conf.codeCount) { 422 | handleCount(); 423 | } 424 | if (!conf.bundle) { // 不用webpack打包时直接输出 425 | logger.notice('构建完成=^_^='); 426 | } 427 | }) 428 | }); 429 | 430 | /* 431 | * 将需要copy的目录和文件平移到output目录 432 | */ 433 | if (config.moveList && config.moveList.length) { 434 | config.moveList.forEach(function (moveItem) { 435 | // var pathArr = moveItem.split('/'); 436 | // pathArr.forEach(function(item, index) { 437 | // if (!item) { 438 | // pathArr.splice(index, 1); 439 | // } 440 | // }) 441 | // var moveItemName = pathArr[pathArr.length - 1]; 442 | // if (fs.existsSync(moveItem)) { 443 | // fs.copySync(moveItem, path.join(conf.output, moveItemName)); 444 | // } 445 | gulp.src(moveItem).pipe(gulp.dest(conf.output)) 446 | gulp.src(path.join(moveItem, '**')).pipe(gulp.dest(conf.output)) 447 | 448 | }) 449 | } 450 | 451 | 452 | 453 | } 454 | 455 | 456 | /* 457 | * del output folder & run first 458 | */ 459 | del(conf.output).then(paths => { 460 | run(); 461 | }); 462 | 463 | /* 464 | * watch the files change 465 | */ 466 | handleWatch(run, conf); 467 | 468 | /* 469 | * run dev server 470 | */ 471 | handleServer(conf.server, config, port); 472 | } 473 | --------------------------------------------------------------------------------