├── .gitignore ├── LICENSE ├── README.md ├── index.js └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /node_modules -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 flashlizi 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 | # posthtml-transfomer 2 | 3 | posthtml-transfomer is plugin for [PostHTML](https://github.com/posthtml/posthtml). It process HTML by special directives in node attrs, such as inline scripts and styles, remove useless tags, concat scripts and styles etc. 4 | 5 | ## Directives 6 | 7 | * `ph-inline` - Load `script` and `link` code and make them inline. 8 | * `ph-remove` - Remove tags which specified this directive. 9 | * `ph-concat-start` and `ph-concat-end` - Concat `script` and `link` code. 10 | 11 | ## Examples 12 | 13 | `ph-inline` directive: 14 | 15 | ``` javascript 16 | // src/lib.js 17 | console.log('hello posthtml-transformer'); 18 | 19 | // index.html 20 | 21 | 22 | // index.html after transform 23 | 26 | ``` 27 | 28 | `ph-remove` directive: 29 | 30 | ``` javascript 31 | // index.html 32 | 33 | 34 | 35 | 36 | // index.html after transform 37 | 38 | 39 | ``` 40 | 41 | `ph-concat-start` and `ph-concat-end` directive: 42 | 43 | ``` javascript 44 | // src/mod1.js 45 | console.log('mod1'); 46 | 47 | // src/mod2.js 48 | console.log('mod2'); 49 | 50 | // src/mod3.js 51 | console.log('mod3'); 52 | 53 | // index.html 54 | 55 | 56 | 57 | 58 | 59 | 60 | // index.html after transform 61 | 62 | 67 | 68 | ``` 69 | 70 | ## Gulp Usage 71 | 72 | Install posthtml-transformer: 73 | ``` 74 | npm install posthtml-transformer --save-dev 75 | ``` 76 | 77 | 78 | ``` javascript 79 | var gulp = require('gulp'); 80 | var posthtml = require('gulp-posthtml'); 81 | var phTransformer = require('posthtml-transformer'); 82 | 83 | gulp.task('posthtml', function(){ 84 | var plugins = [ 85 | phTransformer({ 86 | minifyJS: true, 87 | minifyCSS: true 88 | }), 89 | // phTransformer.match({tag:'html'}, function(node){ 90 | // node.attrs = {'hello': 'world'}; 91 | // return node; 92 | // }), 93 | // phTransformer.walk(function(node){ 94 | // console.log('walk:', node.tag); 95 | // return node; 96 | // }) 97 | ]; 98 | return gulp.src('index.html') 99 | .pipe(posthtml(plugins)) 100 | .on('error', function(err){ 101 | console.error('error:', err); 102 | }) 103 | .pipe(gulp.dest('./build/')) 104 | }); 105 | ``` 106 | 107 | ## Options 108 | 109 | * `minifyJS` - minify javascript in HTML, default is `true`. 110 | * `minifyCSS` - minify css in HTML, default is `true`. 111 | 112 | ## APIs 113 | 114 | * `match` - same as `PostHTML`'s `match`. 115 | * `walk` - same as `PostHTML`'s `walk`. 116 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | var path = require('path'); 3 | var url = require('url'); 4 | var http = require('http'); 5 | var UglifyJS = require('uglify-js'); 6 | var CleanCSS = require('clean-css'); 7 | 8 | var transformOptions = {}; 9 | 10 | function initOptions(options){ 11 | options = options || {}; 12 | options.root = options.root || './'; 13 | options.encoding = options.encoding || 'utf-8'; 14 | options.minifyJS = options.minifyJS === undefined ? true : !!options.minifyJS; 15 | options.minifyCSS = options.minifyCSS === undefined ? true : !!options.minifyCSS; 16 | transformOptions = options; 17 | return options; 18 | } 19 | 20 | function transformer(options){ 21 | options = initOptions(options); 22 | 23 | return function transform(tree, callback){ 24 | var completed = false, tasks = 0; 25 | function done(isTaskDone){ 26 | if(isTaskDone) tasks--; 27 | if(!tasks && completed){ 28 | callback(null, tree); 29 | } 30 | } 31 | 32 | var removeLineFlag = false; 33 | var concatFlag = false, concatContent = ''; 34 | 35 | tree.walk(function(node){ 36 | if(removeLineFlag && node === '\n'){ 37 | removeLineFlag = false; 38 | return ''; 39 | } 40 | 41 | var attrs = node.attrs; 42 | if(!attrs) return node; 43 | 44 | if(attrs.hasOwnProperty('ph-remove')){ 45 | removeLineFlag = true; 46 | return {tag:false, content:''}; 47 | } 48 | 49 | if(attrs.hasOwnProperty('ph-inline')){ 50 | var src, content; 51 | if(node.tag === 'script'){ 52 | tasks++; 53 | readFileContent(attrs.src, function(data){ 54 | setNodeContent(node, data); 55 | delete attrs['ph-inline']; 56 | delete attrs['src']; 57 | delete attrs['type']; 58 | done(true); 59 | }); 60 | }else if(node.tag === 'link'){ 61 | tasks++; 62 | readFileContent(attrs.href, function(data){ 63 | node.tag = 'style'; 64 | setNodeContent(node, data); 65 | delete attrs['ph-inline']; 66 | delete attrs['rel']; 67 | delete attrs['href']; 68 | delete attrs['type']; 69 | done(true); 70 | }); 71 | } 72 | return node; 73 | } 74 | 75 | if(concatFlag || attrs.hasOwnProperty('ph-concat-start')){ 76 | concatFlag = true; 77 | var src = attrs.src || attrs.href; 78 | concatContent += '\n' + fs.readFileSync(path.resolve(options.root, src), options.encoding); 79 | 80 | if(attrs.hasOwnProperty('ph-concat-end')){ 81 | concatFlag = false; 82 | if(concatContent.charAt(concatContent.length - 1) !== '\n'){ 83 | concatContent += '\n'; 84 | } 85 | if(node.tag === 'script'){ 86 | var needMinify = transformOptions.minifyJS || attrs.hasOwnProperty('ph-minify'); 87 | concatContent = needMinify ? minifyJS(concatContent) : concatContent; 88 | return {tag:'script', content:concatContent}; 89 | }else if(node.tag === 'link'){ 90 | var needMinify = transformOptions.minifyCSS || attrs.hasOwnProperty('ph-minify'); 91 | concatContent = needMinify ? minifyCSS(concatContent) : concatContent; 92 | return {tag:'style', content:concatContent}; 93 | } 94 | }else{ 95 | removeLineFlag = true; 96 | return {tag:false, content:''}; 97 | } 98 | } 99 | 100 | if(node.tag === 'script' || node.tag === 'style'){ 101 | if(node.content){ 102 | setNodeContent(node, node.content.toString()); 103 | } 104 | } 105 | 106 | return node; 107 | }); 108 | 109 | completed = true; 110 | done(); 111 | return tree; 112 | }; 113 | }; 114 | 115 | function readFileContent(src, options){ 116 | options = typeof options === 'function' ? {callback:options} : (options || {}); 117 | options.encoding = options.encoding || 'utf-8'; 118 | var matched = src.match(/^((http|https):)?\/\/([\s\S]+)$/); 119 | 120 | if(matched){ 121 | if(matched[1] === undefined) src = 'http:' + src; 122 | var requestOptions = url.parse(src); 123 | requestOptions.headers = { 124 | 'Accept': '*/*', 125 | 'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 8_0 like Mac OS X) AppleWebKit/600.1.3 (KHTML, like Gecko) Version/8.0 Mobile/12A4345d Safari/600.1.4', 126 | }; 127 | var request = http.get(requestOptions, function(respose){ 128 | var content = ''; 129 | respose.setEncoding('utf8'); 130 | respose.on('data', function(chunk){ 131 | content += chunk; 132 | }); 133 | respose.on('end', function(){ 134 | options.callback && options.callback(content); 135 | }); 136 | }).on('error', function(err){ 137 | console.log('error:', err); 138 | }); 139 | }else{ 140 | var content = fs.readFileSync(path.resolve(transformOptions.root, src), options.encoding); 141 | options.callback && options.callback(content); 142 | } 143 | } 144 | 145 | function setNodeContent(node, content){ 146 | var needMinify = node.attrs && node.attrs.hasOwnProperty('ph-minify'); 147 | if(needMinify) delete node.attrs['ph-minify']; 148 | 149 | if(node.tag === 'script' && (transformOptions.minifyJS || needMinify)){ 150 | node.content = minifyJS(content); 151 | }else if(node.tag === 'style' && (transformOptions.minifyCSS || needMinify)){ 152 | node.content = minifyCSS(content); 153 | }else{ 154 | node.content = content; 155 | } 156 | } 157 | 158 | function minifyJS(js){ 159 | var code = UglifyJS.minify(js, {fromString:true}).code; 160 | return code; 161 | } 162 | 163 | function minifyCSS(css){ 164 | var code = new CleanCSS({advanced:false}).minify(css).styles; 165 | return code; 166 | } 167 | 168 | transformer.match = function(expression, callback){ 169 | return function(tree){ 170 | tree.match(expression, callback); 171 | } 172 | }; 173 | 174 | transformer.walk = function(callback){ 175 | return function(tree){ 176 | tree.walk(callback); 177 | } 178 | }; 179 | 180 | module.exports = transformer; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "posthtml-transformer", 3 | "version": "0.1.2", 4 | "description": "A posthtml plugin for transforming HTML contents", 5 | "keywords": [ 6 | "posthtml", 7 | "transformer" 8 | ], 9 | "dependencies": { 10 | "clean-css": "~3.4.8", 11 | "uglify-js": "~2.6.1" 12 | }, 13 | "devDependencies": { 14 | "posthtml": "~0.8.0" 15 | }, 16 | "main": "index.js", 17 | "repository": { 18 | "type": "git", 19 | "url": "git://github.com/flashlizi/posthtml-transformer.git" 20 | }, 21 | "license": "MIT" 22 | } 23 | --------------------------------------------------------------------------------