├── .gitignore
├── .travis.yml
├── LICENSE
├── README.md
├── console.js
├── dist
├── index.html
├── index_absolute.html
├── index_inline.html
└── static
│ ├── merge.css
│ ├── merge.js
│ ├── merge2.css
│ └── merge2.js
├── index.js
├── package.json
└── test
├── assets
└── js
│ ├── a.js
│ └── b.js
├── css
├── a.css
└── b.css
├── gulpfile.js
├── index.html
├── index_absolute.html
└── index_inline.html
/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules
2 | /*.log
3 | .idea/
4 | *~
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - "0.10"
4 | before_script:
5 | - npm install -g gulp
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 jason
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 推荐 [node-project-template](https://github.com/dnxbf321/node-project-template)
2 | gulp-htmloptimize 已非常陈旧,npm scripts 当道,node-project-template 基于 npm scripts 提供整套的前端开发流程
3 |
4 | # gulp-htmloptimize
5 | 构建 html 的 gulp 模块
6 |
7 | ## 用法
8 |
9 | ```js
10 | var htmloptimize = require('gulp-htmloptimize');
11 | gulp.task('htmloptimize', function() {
12 | // 自定义配置
13 | var opts = {};
14 | return gulp.src('./**/*.html')
15 | .pipe(htmloptimize(opts))
16 | .pipe(gulp.dest('./dist'));
17 | });
18 | ```
19 |
20 | ## 优化哪些方面
21 | 1. 在合适的地方加上特定格式的html注释就能编译css、 js 文件
22 | 2. 编译css文件:
23 | * 支持 @import url(file.css)
24 | * 自动加浏览器厂商前缀,解决兼容性问题,使用 autoprefixer-core 库
25 | * 压缩、优化css,使用 clean-css 库
26 | 3. 编译js文件
27 | * 压缩混淆 js 代码,使用 uglify-js 库
28 | 4. 行内样式、js优化
29 | 5. 自定义配置
30 |
31 |
32 | ## options
33 | ```
34 | opts = mergeObj({
35 | debug: false,
36 | root: process.cwd().replace(/\\/g, '/'),
37 | assetsDir: '',
38 | uglifyJs: {
39 | mangle: {
40 | except: ['require', 'exports', 'module']
41 | },
42 | code: {
43 | indent_level: 2
44 | }
45 | },
46 | autoprefixer: {
47 | safe: true,
48 | browsers: ['> 5%', 'last 3 versions', 'Firefox ESR', 'iOS >= 6', 'Android >= 4.0', 'ExplorerMobile >= 10']
49 | },
50 | cleanCss: {
51 | advanced: false,
52 | keepSpecialComments: 0,
53 | processImport: false,
54 | rebase: false
55 | }
56 | }, yourOpts);
57 | ```
58 |
59 |
60 | debug |
61 | 什么都不做,directly pipe |
62 |
63 |
64 | root |
65 | web项目根目录,大部分情况应当与package.json同级(tips:硬盘路径,使用path.resolve(process.cwd(), '../yourproject')获得) |
66 |
67 |
68 | assetsDir |
69 | 外联js、css文件存放目录,当html使用绝对路径引入js、css文件时,对应的真实文件路径是path.join(opts.root, opts.assetsDir, 'static_file_href') |
70 |
71 |
72 | uglifyJs |
73 | 见 [uglifyJs的配置](https://github.com/mishoo/UglifyJS) |
74 |
75 |
76 | autoprefixer |
77 | 见 [autoprefixer-core的配置](https://github.com/postcss/autoprefixer-core) |
78 |
79 |
80 | cleanCss |
81 | 见 [clean-css的配置](https://github.com/jakubpawlowicz/clean-css) |
82 |
83 |
84 |
85 | ## 文档
86 | [wiki](https://github.com/dnxbf321/gulp-htmloptimize/wiki)
87 |
88 | ## LICENSE
89 |
90 | [MIT License](https://github.com/dnxbf321/gulp-htmloptimize/blob/master/LICENSE)
91 |
--------------------------------------------------------------------------------
/console.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 |
3 | var pathjoin = path.join('/a/b.js', 'c.js');
4 |
5 | console.log(pathjoin);
--------------------------------------------------------------------------------
/dist/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | test
7 |
8 |
9 | test body.
10 |
11 |
12 |
--------------------------------------------------------------------------------
/dist/index_absolute.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | test
7 |
8 |
9 | test body.
10 |
11 |
12 |
--------------------------------------------------------------------------------
/dist/index_inline.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | test
7 |
8 |
9 | test body.
10 |
11 | code between two scripts
12 |
13 |
14 |
--------------------------------------------------------------------------------
/dist/static/merge.css:
--------------------------------------------------------------------------------
1 | html{padding:30px}body{height:100px;background:#000;-webkit-transform:scale(.5);-ms-transform:scale(.5);transform:scale(.5)}
--------------------------------------------------------------------------------
/dist/static/merge.js:
--------------------------------------------------------------------------------
1 | (function(e,t){var n=/mobile/i.test(t),r={ios:/iphone|ipod|ipad/i.test(t),android:/android/i.test(t),wechat:/micromessenger/i.test(t),weibo:/weibo/i.test(t)};e.browser=r})(window,navigator.userAgent.toLowerCase()),console.log("hello world!")
--------------------------------------------------------------------------------
/dist/static/merge2.css:
--------------------------------------------------------------------------------
1 | body{height:100px;background:#000;-webkit-transform:scale(.5);-ms-transform:scale(.5);transform:scale(.5)}html{padding:30px}
--------------------------------------------------------------------------------
/dist/static/merge2.js:
--------------------------------------------------------------------------------
1 | (function(e,t){var n=/mobile/i.test(t),r={ios:/iphone|ipod|ipad/i.test(t),android:/android/i.test(t),wechat:/micromessenger/i.test(t),weibo:/weibo/i.test(t)};e.browser=r})(window,navigator.userAgent.toLowerCase()),console.log("hello world!")
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | module.exports = function(opts) {
2 | var through = require('through2'),
3 | gutil = require('gulp-util'),
4 | glob = require('glob'),
5 | CleanCSS = require('clean-css'),
6 | autoprefixer = require('autoprefixer-core'),
7 | uglifyJs = require('uglify-js'),
8 | uglify = uglifyJs.uglify,
9 | parser = uglifyJs.parser,
10 | path = require('path'),
11 | fs = require('fs');
12 |
13 | var START_REG = //gim,
14 | END_REG = //gim,
15 | JSFILE_REG = /
139 | */
140 | function inlineOptimze(source) {
141 | var blocks = [];
142 | var sections = source.split(INLINE_START_REG);
143 | sections.forEach(function(s) {
144 | if (s.match(INLINE_END_REG) && !s.match(JSFILE_REG)) {
145 | // s[0] css rules or js code
146 | // s[1] or
147 | // s[2] other html code after
148 | s = s.split(INLINE_END_REG);
149 | switch (s[1]) {
150 | case '':
151 | s[0] = optimizeCss(s[0]);
152 | break;
153 | case '':
154 | s[0] = optimizeJs(s[0]);
155 | break;
156 | }
157 | blocks.push(s.join(''));
158 | } else {
159 | blocks.push(s);
160 | }
161 | });
162 | return blocks.join('');
163 | }
164 |
165 | function Builder(file, push) {
166 | this.push = push;
167 |
168 | this.filebase = file.base.replace(/\\/g, '/');
169 | this.filepath = file.path.replace(/\\/g, '/');
170 | this.filedir = path.dirname(this.filepath).replace(/\\/g, '/');
171 |
172 | var result = this.analysis(file);
173 | this.pushHtmlFile(result.blocks).pushStaticFile(result.files);
174 | }
175 |
176 | Builder.prototype = {
177 | /*
178 | * 分析 html 代码
179 | * return {blocks: ['html代码块'], files: [{type: 'css', paths: ['static file path']}]}
180 | */
181 | analysis: function(file) {
182 | var sections = file.contents.toString().split(END_REG);
183 |
184 | var blocks = [],
185 | files = [];
186 |
187 | var self = this;
188 |
189 | sections.forEach(function(s) {
190 | if (s.match(START_REG)) {
191 | // s[0] html code
192 | // s[1] string 'css' or 'js'
193 | // s[2] string dest url
194 | // s[3] tags of link or script
195 | s = s.split(START_REG);
196 | blocks.push(s[0]);
197 | switch (s[1]) {
198 | case 'css':
199 | blocks.push('');
200 | break;
201 | case 'js':
202 | blocks.push('');
203 | break;
204 | default:
205 | throw new gutil.PluginError('gulp-htmlbuild', 'only build:css build:js accept\n');
206 | }
207 | var paths = self.matchContentPaths(s[1] === 'css' ? CSSFILE_REG : JSFILE_REG, s[3]);
208 | var source = concatSource(paths);
209 | files.push({
210 | type: s[1],
211 | dest: resolveSrc(s[2], self.filedir),
212 | source: source
213 | });
214 | } else {
215 | blocks.push(s);
216 | }
217 | });
218 |
219 | return {
220 | blocks: blocks,
221 | files: files
222 | }
223 | },
224 | matchContentPaths: function(reg, content) {
225 | var self = this;
226 | var paths = [];
227 | content.replace(reg, function(g, p) {
228 | p = resolveSrc(p, self.filedir);
229 | paths.push(p);
230 | });
231 | return paths;
232 | },
233 | pushHtmlFile: function(blocks) {
234 | var dest = relativeDest(this.filepath, this.filebase);
235 | var optimized = inlineOptimze(blocks.join(''));
236 | var htmlFile = new gutil.File({
237 | path: dest,
238 | contents: new Buffer(optimized)
239 | });
240 | this.push(htmlFile);
241 | return this;
242 | },
243 | pushStaticFile: function(groups) {
244 | var self = this;
245 | groups.forEach(function(group) {
246 | var dest = relativeDest(group.dest, self.filebase);
247 |
248 | var optimizeFun = group.type === 'css' ? optimizeCss : optimizeJs;
249 | var optimized = optimizeFun(group.source);
250 |
251 | self.push(new gutil.File({
252 | path: dest,
253 | contents: new Buffer(optimized)
254 | }));
255 | });
256 | return this;
257 | }
258 | };
259 |
260 | return through.obj(function(file, enc, cb) {
261 | switch (true) {
262 | case opts.debug:
263 | this.push(file);
264 | break;
265 | case file.isStream():
266 | this.emit('error', new gutil.PluginError('gulp-htmlbuild', 'Streams are not supported!\n'));
267 | break;
268 | case file.isNull():
269 | cb(null, file);
270 | return;
271 | break;
272 | default:
273 | try {
274 | new Builder(file, this.push.bind(this));
275 | } catch (e) {
276 | this.emit('error', e);
277 | }
278 | }
279 | cb();
280 | });
281 | };
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "gulp-htmloptimize",
3 | "description": "gulp html files",
4 | "version": "0.0.3",
5 | "homepage": "https://github.com/dnxbf321/gulp-htmloptimize",
6 | "main": "index.js",
7 | "scripts": {
8 | "start": "node index.js",
9 | "test": "node test/gulpfile.js"
10 | },
11 | "repository": {
12 | "type": "git",
13 | "url": "https://github.com/dnxbf321/gulp-htmloptimize.git"
14 | },
15 | "keywords": [
16 | "html",
17 | "gulp",
18 | "optimize"
19 | ],
20 | "author": {
21 | "name": "dnxbf321",
22 | "email": "dnxbf321@gamil.com",
23 | "url": "https://github.com/dnxbf321"
24 | },
25 | "license": "MIT",
26 | "dependencies": {
27 | "through2": "0.6.3",
28 | "glob": "5.0.3",
29 | "gulp": "3.8.11",
30 | "gulp-util": "3.0.4",
31 | "clean-css": "3.1.*",
32 | "autoprefixer-core": "5.1.*",
33 | "uglify-js": "1.3.5"
34 | }
35 | }
--------------------------------------------------------------------------------
/test/assets/js/a.js:
--------------------------------------------------------------------------------
1 | (function(global, userAgent) {
2 | var isMobile = /mobile/i.test(userAgent);
3 | var browser = {
4 | ios: /iphone|ipod|ipad/i.test(userAgent),
5 | android: /android/i.test(userAgent),
6 | wechat: /micromessenger/i.test(userAgent),
7 | weibo: /weibo/i.test(userAgent)
8 | };
9 | global.browser = browser;
10 | }(window, navigator.userAgent.toLowerCase()));
--------------------------------------------------------------------------------
/test/assets/js/b.js:
--------------------------------------------------------------------------------
1 | console.log('hello world!');
--------------------------------------------------------------------------------
/test/css/a.css:
--------------------------------------------------------------------------------
1 | @import url(b.css);
2 |
3 | body {
4 | height: 100px;
5 |
6 | background: #000;
7 |
8 | transform: scale(0.5);
9 | }
--------------------------------------------------------------------------------
/test/css/b.css:
--------------------------------------------------------------------------------
1 | html {
2 | padding: 30px;
3 | }
--------------------------------------------------------------------------------
/test/gulpfile.js:
--------------------------------------------------------------------------------
1 | var gulp = require('gulp'),
2 | htmlbuild = require('../index.js'),
3 | path = require('path');
4 | gulp.src('test/**/*.html')
5 | .pipe(htmlbuild({}))
6 | .pipe(gulp.dest('./dist'));
--------------------------------------------------------------------------------
/test/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | test
9 |
10 |
11 | test body.
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/test/index_absolute.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | test
10 |
11 |
12 | test body.
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/test/index_inline.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
13 | test
14 |
15 |
16 | test body.
17 |
22 | code between two scripts
23 |
28 |
29 |
--------------------------------------------------------------------------------