├── .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 |
--------------------------------------------------------------------------------