UNPKG

15.7 kBJavaScriptView Raw
1Array.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
13module.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('<script type="text/javascript" src="' + jsPath + '"></script>');
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('<link rel="stylesheet" type="text/css" href="' + cssPath + '"/>');
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) { //如果bundle成一个文件
325 replaceResource();
326 }
327 else if (conf.bundle) {
328 replaceResource('js');
329 addVersion('css');
330 }
331 else {
332 addVersion();
333 }
334
335 if (handleLimit === 'js') {
336 handleJS(jsArr, conf, filename, env, wpconfig);
337 }
338 else if (handleLimit === 'css') {
339 handleCSS(cssArr, conf, filename, env);
340 }
341 else if (handleLimit === 'imgs') {
342 handleImage(imgArr, conf, filename, env);
343 }
344 else {
345 handleJS(jsArr, conf, filename, env, wpconfig);
346 handleCSS(cssArr, conf, filename, env);
347 handleImage(imgArr, conf, filename, env);
348 }
349
350 /*
351 * 被忽略的文件以不变的目录结构放到
352 */
353 if (ignoreArr.length) {
354 gulp.src(ignoreArr)
355 .pipe(gulp.dest(function(file){
356 return path.dirname(path.join(conf.output, path.relative(conf.src, file.path)));
357 }));
358 }
359
360 file.contents = new Buffer($.html({xmlMode: false, decodeEntities: false}));
361 this.push(file);
362
363 cb();
364 });
365
366 }
367
368 var run = function(env) {
369 logger.info('build begin!!');
370 var getTarget = function(type) {
371 if (!conf.buildTarget[type]){
372 var target = function(file){
373 return path.dirname(path.join(conf.output, path.relative(conf.src, file.path)));
374 }
375 }
376 else {
377 var target = path.join(conf.output, conf.buildTarget[type]);
378 }
379 return target;
380 }
381
382 var htmlTarget = getTarget('html');
383 var imageTarget = getTarget('imgs');
384
385 var promise = new Promise((resolve, reject) => {
386 if (env !== 'js' && env !== 'css') {
387 if (conf.imgSrc) {
388 logger.notice('执行图片复制');
389 gulp.src(conf.imgSrc)
390 .on('error', reject)
391 .pipe(gulp.dest(imageTarget))
392 .on('end', resolve)
393 }
394 }
395 else {
396 resolve();
397 }
398 });
399
400 promise.then(function() {
401 var entries = conf.entries || config.entries;
402 var stream = gulp.src(entries)
403 .pipe(count({
404 // message: '<%= files %>? That\'s ## too many!',
405 getFileCount: function(entriesCount) {
406 gulpFileCount.filecount = entriesCount;
407 }
408 }))
409 .pipe(gulpif(!conf.inline, findResource(env)))
410
411 if (conf.custom && conf.custom.html && conf.custom.html.length) {
412 conf.custom.html.forEach(function (task) {
413 stream = stream.pipe(task.func(task.opts));
414 });
415 }
416
417 stream = stream.pipe(gulpif(conf.inline, addInlineAttr()))
418 .pipe(gulpif(conf.inline, inlinesource()))
419 .pipe(gulpif(conf.minifyHTML, htmlmin()))
420 .on('error', function() {
421 logger.notice('构建失败::>_<::');
422 })
423 .pipe(gulp.dest(htmlTarget))
424 .on('end', function() {
425 if (conf.codeCount) {
426 handleCount();
427 }
428 if (!conf.bundle) { // 不用webpack打包时直接输出
429 logger.notice('构建完成=^_^=');
430 }
431 })
432 });
433
434 /*
435 * 将需要copy的目录和文件平移到output目录
436 */
437 if (config.moveList && config.moveList.length) {
438 config.moveList.forEach(function (moveItem) {
439 // var pathArr = moveItem.split('/');
440 // pathArr.forEach(function(item, index) {
441 // if (!item) {
442 // pathArr.splice(index, 1);
443 // }
444 // })
445 // var moveItemName = pathArr[pathArr.length - 1];
446 // if (fs.existsSync(moveItem)) {
447 // fs.copySync(moveItem, path.join(conf.output, moveItemName));
448 // }
449 gulp.src(moveItem).pipe(gulp.dest(conf.output))
450 gulp.src(path.join(moveItem, '**')).pipe(gulp.dest(conf.output))
451
452 })
453 }
454
455
456
457 }
458
459
460 /*
461 * del output folder & run first
462 */
463 del(conf.output).then(paths => {
464 run();
465 });
466
467 /*
468 * watch the files change
469 */
470 handleWatch(run, conf);
471
472 /*
473 * run dev server
474 */
475 handleServer(conf.server, config, port);
476}