UNPKG

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