├── .gitignore ├── LICENSE ├── README.md ├── README_zh-CN.md ├── assets ├── .babelrc ├── build.png ├── build.svg ├── coverage.png ├── coverage.svg └── logo.png ├── index.js ├── karma └── karma.conf.js ├── lib ├── assetsPathParser.js ├── base64.js ├── cdnProxy.js ├── gmutil.js ├── gulp-usemin │ ├── .npmignore │ ├── .travis.yml │ ├── LICENSE │ ├── README.md │ ├── gulpfile.js │ ├── index.js │ ├── lib │ │ ├── blocksBuilder.js │ │ ├── htmlBuilder.js │ │ └── pipeline.js │ ├── package.json │ └── test │ │ ├── expected │ │ ├── app.js │ │ ├── array-js-attributes.html │ │ ├── build-remove-no-trailing-whitespace.html │ │ ├── complex-path.html │ │ ├── complex.html │ │ ├── conditional-complex.html │ │ ├── conditional-css.html │ │ ├── conditional-inline-css.html │ │ ├── conditional-inline-js.html │ │ ├── conditional-js.html │ │ ├── data │ │ │ ├── css │ │ │ │ ├── min-style.css │ │ │ │ └── style.css │ │ │ └── js │ │ │ │ ├── app.js │ │ │ │ ├── app_min_concat.js │ │ │ │ └── min-app.js │ │ ├── glob-inline-css.html │ │ ├── glob-inline-js.html │ │ ├── many-blocks-removal.html │ │ ├── min-app.js │ │ ├── min-complex-path.html │ │ ├── min-complex.html │ │ ├── min-css-with-media-query.html │ │ ├── min-html-simple-css.html │ │ ├── min-html-simple-js.html │ │ ├── min-html-simple-removal.html │ │ ├── min-paths-with-querystring.html │ │ ├── min-simple-css-path.html │ │ ├── min-simple-css.html │ │ ├── min-simple-js-path.html │ │ ├── min-simple-js.html │ │ ├── min-style.css │ │ ├── multiple-alternative-paths.html │ │ ├── multiple-files.html │ │ ├── paths-with-querystring.html │ │ ├── simple-css-path.html │ │ ├── simple-css.html │ │ ├── simple-inline-css.html │ │ ├── simple-inline-js.html │ │ ├── simple-js-path.html │ │ ├── simple-js-removal.html │ │ ├── simple-js.html │ │ ├── single-quotes-css.html │ │ ├── single-quotes-inline-css.html │ │ ├── single-quotes-inline-js.html │ │ ├── single-quotes-js.html │ │ ├── style.css │ │ └── subfolder │ │ │ ├── app.js │ │ │ └── index.html │ │ ├── fixtures │ │ ├── alternative │ │ │ └── js │ │ │ │ └── util.js │ │ ├── array-js-attributes.html │ │ ├── async-less.html │ │ ├── build-remove-no-trailing-whitespace.html │ │ ├── comment-js.html │ │ ├── complex-path.html │ │ ├── complex.html │ │ ├── conditional-complex.html │ │ ├── conditional-css.html │ │ ├── conditional-inline-css.html │ │ ├── conditional-inline-js.html │ │ ├── conditional-js.html │ │ ├── css-with-media-query-error.html │ │ ├── css-with-media-query.html │ │ ├── css │ │ │ ├── clear.css │ │ │ └── main.css │ │ ├── glob-css.html │ │ ├── glob-inline-css.html │ │ ├── glob-inline-js.html │ │ ├── glob-js.html │ │ ├── js │ │ │ ├── lib.js │ │ │ └── main.js │ │ ├── js2 │ │ │ ├── lib2.js │ │ │ └── main2.js │ │ ├── less │ │ │ ├── clear.less │ │ │ └── main.less │ │ ├── many-blocks-removal.html │ │ ├── many-blocks.html │ │ ├── min-html-simple-css.html │ │ ├── min-html-simple-js.html │ │ ├── min-html-simple-removal.html │ │ ├── multiple-alternative-paths-inline.html │ │ ├── multiple-alternative-paths.html │ │ ├── multiple-files.html │ │ ├── paths-with-querystring.html │ │ ├── simple-css-alternate-path.html │ │ ├── simple-css-path.html │ │ ├── simple-css.html │ │ ├── simple-inline-css.html │ │ ├── simple-inline-js.html │ │ ├── simple-js-alternate-path.html │ │ ├── simple-js-path.html │ │ ├── simple-js-removal.html │ │ ├── simple-js.html │ │ ├── single-quotes-css.html │ │ ├── single-quotes-inline-css.html │ │ ├── single-quotes-inline-js.html │ │ ├── single-quotes-js.html │ │ └── subfolder │ │ │ ├── index.html │ │ │ └── script.js │ │ └── main.js ├── htmlPathParser.js ├── iconFonter.js ├── inline.js ├── revReplace.js ├── spriter │ ├── README.md │ ├── index.js │ ├── lib │ │ ├── get-background-image-declarations.js │ │ ├── get-meta-info-for-declaration.js │ │ ├── map-over-styles-and-transform-background-image-declarations.js │ │ ├── spriter-util.js │ │ ├── transform-file-with-sprite-sheet-data.js │ │ └── transform-map.js │ └── package.json └── store.js ├── meta ├── favicon.ico └── home │ ├── img │ └── logo.png │ ├── index.html │ ├── main.es6 │ └── main.scss ├── package-lock.json ├── package.json ├── presetlib ├── jquery.js ├── react-0.14.6 │ └── build │ │ ├── react-dom-server.js │ │ ├── react-dom-server.min.js │ │ ├── react-dom.js │ │ ├── react-dom.min.js │ │ ├── react-with-addons.js │ │ ├── react-with-addons.min.js │ │ ├── react.js │ │ └── react.min.js └── react.js ├── scripts ├── gulp │ └── gulpfile.js ├── install.sh ├── package.json └── preinstall.sh └── test.js /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | node_modules 3 | .npm 4 | .bin 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright Lucas, Inc. and other contributors. 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | [![Coverage Status](https://raw.githubusercontent.com/xunuoi/gulpman/master/assets/logo.png)](http://karat.cc/article/56a351c3e48d2d05682aa0ac/) 3 | 4 | ----- 5 | 6 | [![NPM version](https://img.shields.io/npm/v/gulpman.svg?style=flat-square)](http://badge.fury.io/js/gulpman) 7 | Build status 8 | 9 | 10 | # gulpman 11 | 12 | [浏览简体中文文档](https://github.com/xunuoi/gulpman/blob/master/README_zh-CN.md) 13 | 14 | - Create Modular Front-End Build System, organize the source by module, using relative path, `html/js/css/img/fonts/tpl` are in one same folder, like Baidu `FIS`. Good concept for FE source management / development. 15 | - Concept Introduction: [前端工程之模块化](http://fex.baidu.com/blog/2014/03/fis-module/) 16 | - Component Oriented Solution, based on `gulp`. More simple, flexible, expandable and stable. Everyone know gulp can do secondary development. 17 | - Support `base64` image in `html/CSS` 18 | - Support `JS/CSS` inlnied in html 19 | - Support `require('main.css')`, require css file in js 20 | - Intergrated with `spritesmith`, support auto sprite img 21 | - Intergrated with `icon-font`, support SVG 2 Iconfont. 22 | - Intergrated with `usemin`,support complex combo/package. 23 | - Supoort FE Tpl embed function, the `.tpl` file will packaged into js file,support async js loading. 24 | - Intergrated with `SCSS|ES6|ReactJS|Vuejs|Babel|Browserify|cssnano|uglify|imagmein` and other plugins,One-Stop Solution Service, very Simple and Strong 25 | - High scalability, compatiable with almost `gulp` plugins, you can use them in `gulpman`. For example, you can put `browser-sync` in your gulpman build system 26 | - Intergrated with `karma` framework,support `babel/es6` unit test and coverage result. 27 | 28 | 29 | ## Introduction 30 | - Support Mac、Linux 31 | - No full test under Windows. You can install `gulp`、`gulp-sass` manually 32 | - Required Node >= 4.0.0 33 | 34 | 35 | ## Install 36 | 37 | - `npm install gulpman --save-dev` 38 | - Run `gulp gm:install` to finish the setup 39 | - *If in China, please use `cnpm` to install it: `cnpm install gulpman --save-dev` 40 | 41 | 42 | #### Note 43 | * If `gulp-sass` install failed, please run `cnpm install gulp-sass gulp-imagemin` by manual to fix that. 44 | 45 | * If error happened in npm install,such as `/usr/local/lib/node_modules` permission error, fix this by `sudo chown -R "$(whoami)"`+`Path` 46 | * `sudo npm install` is not recommended 47 | * The imagemin-pngquant module need`libpng-devel`,if in Linux, please run `yum install libpng-devel` at first 48 | * If install failed, check the `npm-debug.log` to see if there are some `ENOMEM`errors 49 | 50 | 51 | 52 | ## Config 53 | 54 | ### 0. Support Auto Mode, no Config 55 | 56 | * You can skip `Config`, and directly jump to `Usage` 57 | 58 | 59 | ### 1. Config gulpfile.js: 60 | 61 | - require the `gulpman` in your gulpfile.js,then it will load `gm:publish`, `gm:develop` into gulp tasks. 62 | - `gulp gm:publish` or `gulp gm:develop` in terminal then it will work 63 | 64 | 65 | ```Javascript 66 | /** 67 | * Gulpfile.js 68 | */ 69 | 70 | 71 | var gulp = require('gulp'), 72 | gman = require('gulpman') 73 | 74 | // your other tasks ... 75 | // xxx ... 76 | 77 | 78 | /** 79 | * config gulpman ====================== 80 | * Use config API 81 | * assets path, CDN, URL prefix 82 | */ 83 | 84 | gman.config({ 85 | 86 | // whether use absolute path, default `true` 87 | 'is_absolute': true, 88 | 89 | // cdn prefix support[string|array|function]arguments 90 | 'cdn_prefix': '', 91 | 92 | // url prefix, defautl `/static`. This involves the server config ,such as the static path of nginx 93 | 'url_prefix': '/static', 94 | 95 | 96 | /** use spritesmith for css-img sprite 97 | * Based on Spritesmith: https://github.com/Ensighten/spritesmith 98 | * Automatecially generate Sprite Image & CSS 99 | **/ 100 | //'spritesmith': { }, 101 | 102 | /** usemin config **/ 103 | // 'usemin': {} 104 | 105 | 106 | // The COMPONENTS directory 107 | 'components': 'components', 108 | 109 | // For development assets and templates folder, related to Server Config 110 | 'runtime_views': 'views', 111 | 'dist_views': 'views_dist', 112 | 113 | // For production assets and templates folder, related to Server Config 114 | 'runtime_assets': 'assets', 115 | 'dist_assets': 'assets_dist', 116 | 117 | // The js library dir, set as a global module. Also you can set as `bower_components` 118 | 'lib': 'lib', 119 | 120 | // You can add one customer global directory, so you can require module name directly, like: `require ('xxx')`. The xxx is in this directory 121 | 'global': 'common' 122 | }) 123 | 124 | 125 | ``` 126 | 127 | ### 2. How to config CDN better 128 | 129 | * `cdn_prefix` support String, Array, Function 130 | * if argument is array, the CDN will be an random value 131 | * if argument is function,it would input one argument, `mediaFile` 132 | 133 | ```Javascript 134 | 135 | 'cdn_prefix': function (fileName) { 136 | 137 | console.log(fileName) 138 | 139 | var c_list = [ 140 | 'http://s0.com', 141 | 'http://s1.com', 142 | 'http://s2.com', 143 | 'http://s3.com', 144 | 'http://s4.com' 145 | ] 146 | // You can customized your strategy 147 | if(hostFile.match(/\.html$/gm)){ 148 | return c_list[0] 149 | }else { 150 | return c_list[1] 151 | } 152 | }, 153 | ``` 154 | 155 | ### 3. About `is_absolute` 156 | 157 | * `is_absolute` is the dist path of source in html. default true. the dist path is like `/static/home/main.js` 158 | 159 | * [*]Need consistent config with Server, like nginx, apache 160 | 161 | * If no local server, you can set is_absolute as false, use relative path. Like `../../assets/static/home/main.js` 162 | 163 | 164 | ### 4. gulpman directory 165 | 166 | * Use gulpman to arrange your directory as component,The root component dir can be`./components`(default). If you have one component named foo, then `./components/foo`,all related assets such as `html|js|css|fonts|image` should be put in `foo` folder. 167 | 168 | * This solution for assets can be high efficiency and easy to maintain. 169 | 170 | * `gm:develop` to start `develop` mode, the `views` dir and `assets` dir can be generated automatically 171 | 172 | * `gm:publish` to publish assets in production env. The `views_dist` and `assets_dist` can generated. 173 | 174 | 175 | ### 5. What is global directory 176 | 177 | - For `Browserify` packing, the js module in `global dir` can be directly `require` or `import` in es6/js code 178 | 179 | - In `gulpman.config`, the `lib`和`global` are global directory. Take an example: 180 | * In `components/lib` directory, you have one module `foo.js`,then it is `components/lib/foo.js`. So when you use foo in your es6 file, you can use it like: `import foo from 'foo'`, no need write as `import foo from '../lib/foo'` 181 | 182 | - similarly, `global` option can set your dir as global module dir. You can set `bower` dir as your `lib` dir. 183 | 184 | - Please make no conficts in your global dir 185 | 186 | 187 | ### 6. Support for complex and multi level directory in config 188 | 189 | * Such as: 190 | 191 | ```Javascript 192 | gulpman.config({ 193 | 'is_absolute': false, 194 | 'components': 'components/cc', 195 | 'runtime_views': 'runtime_views/rv', 196 | 'dist_views': 'dist_views/dv/dv', 197 | 198 | 'dist_assets': 'dist_assets/da', 199 | 'runtime_assets': 'runtime_assets/ra/ra', 200 | }) 201 | ``` 202 | 203 | 204 | ## Usage 205 | 206 | ### 1. CLI run Task: 207 | 208 | ```Shell 209 | 210 | # Create components directory and add one demo 211 | # init components dir and a html demo 212 | gulp gm:init 213 | 214 | 215 | # develop and watch mode,watchings files changes and update files 216 | gulp gm:develop 217 | 218 | # Build and Watch one special component, other files are not compiled 219 | gulp gm:develop -c component_name1,component_name2,component_name3... 220 | 221 | 222 | # publish assets in production env 223 | gulp gm:publish 224 | 225 | # publish command support `-a`和`-v` parameters to set output assets/views path. 226 | gulp gm:publish -v your_views_dist -a your_assets_dist 227 | 228 | # clean dist files 229 | gulp gm:clean 230 | 231 | # clean dist files, including subfolders 232 | gulp gm:clean-deep 233 | 234 | # Generate one developing assets/views files, but not in watching mode 235 | # compile for develop, not watch 236 | gulp gm:compile 237 | 238 | ``` 239 | 240 | 241 | ### 2. Watch one special component in development 242 | 243 | * When the project become huge, if we watch all components assets, it will be slow and low efficiency, so we can only watch special component to get better performance 244 | 245 | * Fox example, if we want watch the `home` component: 246 | 247 | ```Shell 248 | 249 | # this will only build and watch `components/home` components 250 | gulp gm:develop -c home 251 | 252 | ``` 253 | 254 | 255 | ### 3. Use `React` in gulpman 256 | * Install React: `npm install react react-dom` 257 | * Use React in ES6: 258 | 259 | ```Javascript 260 | import React from 'react'; 261 | import ReactDOM from 'react-dom'; 262 | 263 | // xxx 264 | ``` 265 | 266 | 267 | ### 4. Use `tpl` file in js|es6|jsx 268 | 269 | * Support `.tpl` file, it will be packaged in dist js files. 270 | 271 | * Usage: `import dialogTpl from './dialog.tpl'` or `var dialogTpl = require('./dialog.tpl')` 272 | 273 | 274 | 275 | ### 5. Usge base64 img in HTML/CSS 276 | 277 | * Just add `?_gm_inline` in assets src path in html/css 278 | * The `base64` code will be inlined in html/css 279 | 280 | 281 | ##### html 282 | 283 | ```html 284 |

285 | Karat 克拉 286 |

287 | ``` 288 | 289 | ##### CSS/SCSS 290 | 291 | ```css 292 | 293 | .test { 294 | background: url(./img/testb64.png?_gm_inline) no-repeat; 295 | } 296 | ``` 297 | 298 | 299 | ### 6. Use inlined CSS/JS in html by querystring 300 | 301 | * Like base64, just add `?_gm_inline` in url path 302 | 303 | ```html 304 | 305 | 306 | 307 | ``` 308 | 309 | * The inlined sources will be auto updated when source files changed. 310 | 311 | 312 | ### 7. Use Sprite img in css 313 | 314 | * Enable Sprite by `gulpman.config({ enableCSSSprite: true })`, the default is false. 315 | * Based on spritesmith, you can transport usemin opts in gulpman.config. 316 | * More detail about Spritesmith: [https://github.com/Ensighten/spritesmith](https://github.com/Ensighten/spritesmith) 317 | * Usage: In scss file, just add `?_gm_sprite` to img url 318 | 319 | ```css 320 | .demo { 321 | background: url(./img/abc.png?_gm_sprite) no-repeat; 322 | 323 | /* other style you can set ...*/ 324 | width: 50px; 325 | height: 50px; 326 | } 327 | ``` 328 | 329 | ### 8. Use Usemin 330 | 331 | * You can tranport usemin opts in gulpman.config 332 | * More detail about usemin: [https://github.com/zont/gulp-usemin](https://github.com/zont/gulp-usemin) 333 | * Uage: just add usemin build comments in html. Support `js`|`css`|`inlinejs`|`inlinecss` syntax 334 | * Note: Just write relative path in usemin build comment. Then gulpman can calculate absolute path for assets. 335 | * If you don't write output path, the gulpman will combo one new ouput file name automatically. 336 | 337 | 338 | ```html 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | ``` 351 | 352 | 353 | ### 9. Use js tpl template 354 | * Put the `.tpl` files in your component, and use `require` or `import` in ES6, then the tpl files will be packaged in js files. 355 | * All tpl will be convertd to text string into js files. 356 | * Base64 img and CSS/JS Embed are supported in Tpl 357 | 358 | - import tpl in es6 359 | ```js 360 | import dialogTpl from './dialog.tpl' 361 | ``` 362 | 363 | - require 364 | ```js 365 | var dialogTpl = require('./dialog.tpl') 366 | ``` 367 | 368 | ### 10. import css files into js/html 369 | - Just import the css, then the gulpman will attach it on page automatically. 370 | ```js 371 | import from './style.css'; 372 | ``` 373 | 374 | ```js 375 | require('./style.css'); 376 | ``` 377 | 378 | ### 11. Use iconfont convert svg to fontface 379 | * Convert SVG to icon-font, use `@font-face` in css 380 | * Run `gulp gm:iconfont:install` before first running 381 | * Put the svg files in `components/iconfonts/source` directory, then run `gulp gm:iconfont` to begin start convert 382 | * The icon-font and css will generated in `iconfonts/gmicon` folder 383 | 384 | 385 | ### 12. Support LAB.js to load async js 386 | * Add LAB.js in your project 387 | * Use LAB API to load js, use `relative path` 388 | * Example: `$LAB.script("../testload/test.js").wait(()=>{console.log('test loaded')})` 389 | 390 | 391 | ### 13. Require CSS in JS 392 | * Require css files in your es6/js files 393 | * The CSS contents will be packaged into js files, and automatically injected to html when page opend. Using style tag 394 | * Should keep the .css extname 395 | * Example: `require('./style.css')` or `import style from './style.css'` 396 | 397 | 398 | ### 14. Use karma for Unit Test 399 | * Run `gulp gm:karma:install` before first running, it will install dependencies and generate `karma.conf.js`. 400 | * In your one component folder, create one folder named `spec`, then put your spec es6 files in the `spec` folder, the file extname must be `.es6` 401 | * Run `gulp gm:karma:start` in CLI to start Karma Unit Test, you can view the coverage result in `coverage` foloder 402 | * Set one special spec folder、browsers and other karma options, you can set them in `karma.conf.js` 403 | 404 | 405 | 406 | ### Tutorial 407 | [Tutorial Link](http://karat.cc/article/56a351c3e48d2d05682aa0ac "karat.cc") 408 | 409 | ### License 410 | MIT 411 | -------------------------------------------------------------------------------- /README_zh-CN.md: -------------------------------------------------------------------------------- 1 | 2 | [![Coverage Status](https://raw.githubusercontent.com/xunuoi/gulpman/master/assets/logo.png)](http://karat.cc/article/56a351c3e48d2d05682aa0ac/) 3 | 4 | ----- 5 | 6 | [![NPM version](https://img.shields.io/npm/v/gulpman.svg?style=flat-square)](http://badge.fury.io/js/gulpman) 7 | Build status 8 | 9 | 10 | # gulpman 11 | 12 | [English Document](https://github.com/xunuoi/gulpman/blob/master/README.md) 13 | 14 | - 支持资源模块化组织方式,通过相对路径运算,像百度的FIS一样,可以将`js/css/img/fonts/tpl`按照功能单位组织到同一个目录中,不再分散维护。Gulpman运行时会自动分发各种资源到正确目录。 15 | - 概念介绍: [前端工程之模块化](http://fex.baidu.com/blog/2014/03/fis-module/) 16 | - 基于`gulp`的前端组件化、模块化解决方案,更简单、灵活、可控性高,会gulp就会定制自己的方案 17 | - 支持图片`base64`方式嵌入到`html/CSS` 18 | - 支持`JS/CSS`内联方式嵌入html文件 19 | - 整合`spritesmith`,简单生成sprite雪碧图 20 | - 整合`icon-font`转换,支持svg转换 21 | - 整合`usemin`,构建合并更加灵活强大 22 | - 支持前端js模板嵌入,`tpl`格式的直接构建打包到最终js文件,支持异步加载js 23 | - 集成`SCSS|ES6|ReactJS|Babel|Browserify|cssnano|uglify|imagmein`等常用组件,做到一站式自动化解决方案,同时清晰、可控,定制、修改简单 24 | - 扩展性高,`gulp`现有的插件都可以拼装、加入到`gulpman`中使用,你可以自己根据实际情况组合、修改,比如可以轻松整合`browser-sync`到构建系统中。 25 | - 整合`karma`单元测试框架,适配`babel和es6`的代码单元测试和`coverage` 26 | 27 | 28 | 29 | ## 说明 30 | - 支持Mac、Linux环境下安装、使用 31 | - Windows环境未做完整安装测试,由于安装脚本使用到shell,windows不支持shell,执行完`npm install gulpman --save-dev`后,可能需要手动安装`gulp`、`gulp-sass`模块 32 | - 如果手动安装`gulp-sass`,建议使用淘宝的`cnpm`来完成,避免国内网络导致`npm`安装失败 33 | - Node版本需要不低于4.0.0 34 | 35 | 36 | ## 安装 37 | - `npm install gulpman --save-dev` 38 | - 如果在中国,请使用cnpm安装:`cnpm install gulpman --save-dev` 39 | - 完成后请运行 `gulp gm:install` 来完成安装 40 | 41 | #### 注: 42 | * 如果安装过程中提示 `gulp-sass` 安装失败, 运行 `cnpm install gulp-sass gulp-imagemin` 来修复。 43 | 44 | * 安装中若npm报出目录权限导致的error,比如涉及到`/usr/local/lib/node_modules`权限的报错,请请检查其权限是否正常并用chown来修复,将拥有者修改为当前登录用户即可。 45 | * 可以使用 `sudo chown -R "$(whoami)"`+`路径`来修复 46 | * 不要使用`sudo npm install`来手工安装因为权限问题而失败的模块。请修改权限后,再用`npm install`来安装即可 47 | * 如果你本地node和npm的安装和权限正常,那gulpman的安装过程应该都是顺利和成功的。 48 | * 图片压缩模块imagemin-pngquant需要依赖`libpng-devel`,如果是Linux环境,建议先运行`yum install libpng-devel`来确保安装 49 | * 安装过程中无故退出,请查看`npm-debug.log`,检查是否是内存不足`ENOMEM`导致。 50 | 51 | 52 | 53 | ## 配置 54 | 55 | ### 0. 支持自动默认模式,无需配置即使用 56 | 57 | * 可直接跳过`Config 配置`处的说明,直接去看后面的`Usage 使用`内容 58 | 59 | 60 | ### 1. 配置 gulpfile.js: 61 | 62 | - 只需要require gulpman模块,就会自动加载`gm:publish`, `gm:develop`(开发监视模式)等task到环境中 63 | - 使用时在命令行中直接输入`gulp gm:publish`即可执行gulpman预置的任务 64 | 65 | 66 | ```Javascript 67 | /** 68 | * Gulpfile.js 69 | */ 70 | 71 | 72 | var gulp = require('gulp'), 73 | gman = require('gulpman') 74 | 75 | // your other tasks ...你的其他task 76 | // xxx ... 77 | 78 | 79 | /** 80 | * 配置gulpman ====================== 81 | * Use config API 82 | * 设置路径、CDN、资源URL前缀等,API简单 83 | */ 84 | 85 | gman.config({ 86 | 87 | // 是否使用绝对路径,默认值true, 推荐使用,方便服务器配置。比如`/static/home/main.js`这种风格。 88 | // 如果无服务端情况下,本地调试,可以设置is_absolute为false, 那么会是类似`../../assets/static/home/main.js`这种风格 89 | 'is_absolute': true, 90 | 91 | // cdn prefix 配置CDN, 支持[字符串|数组|函数] 3中传参方式 92 | 'cdn_prefix': '', 93 | 94 | // 配置资源URL前缀,建议类似 /static这种 95 | // usually set as /static, this involves the server config ,such as the static path of nginx 96 | 'url_prefix': '/static', 97 | 98 | 99 | /** use spritesmith for css-img sprite 100 | * 基于spritesmith实现, 详细参见https://github.com/Ensighten/spritesmith 101 | * 传递自动生成雪碧图的spritesmit的options 102 | **/ 103 | //'spritesmith': { },保持默认即可 104 | 105 | /** usemin config 配置usemin,保持默认即可 **/ 106 | // 'usemin': {} 107 | 108 | 109 | // 模块COMPONENTS目录,同一个模块的html和资源文件在一起。默认 'components'即可 110 | 'components': 'components', 111 | 112 | // develop和publish下的views目录,跟服务端框架的views目录配置一致,比如express 113 | 'runtime_views': 'views', 114 | 'dist_views': 'views_dist', 115 | 116 | // develop和publish下的assets静态目录,跟服务器配置有关,比如nginx的static目录指向,请保持与服务器设定一致。支持多级路径设定,比如assets/public 117 | 'runtime_assets': 'assets', 118 | 'dist_assets': 'assets_dist', 119 | 120 | // 第三方JS类库、模块的目录,推荐设置为`lib`或`bower_components`(这样bower可以直接安装到这个目录) 121 | // 这个目录默认打包时为全局模块目录,可以直接`import xxx from 'xxx'`,而不用加相对路径 122 | // the js library dir, set as a global module. Also you can set as bower_components 123 | 'lib': 'lib', 124 | 125 | // 可以添加一个自定的全局模块目录,该目录下的js模块,也作为全局模块来require,不需要相对路径。 126 | // the global module dir 127 | 'global': 'common' 128 | }) 129 | 130 | 131 | ``` 132 | 133 | ### 2. 如何更好的配置CDN 134 | 135 | * `cdn_prefix`支持 字符串、数组、函数 136 | * 如果传入数组,那么按照随机来分配 137 | * 如果传入函数,函数会获1个参数,`mediaFile`, 就是当前被css或html中引用到的资源文件名,可以根据文件名做cdn分配 138 | 139 | ```Javascript 140 | 141 | 'cdn_prefix': function (fileName) { 142 | 143 | console.log(fileName) 144 | 145 | var c_list = [ 146 | 'http://s0.com', 147 | 'http://s1.com', 148 | 'http://s2.com', 149 | 'http://s3.com', 150 | 'http://s4.com' 151 | ] 152 | // 你自可以自实现分配策略 153 | if(hostFile.match(/\.html$/gm)){ 154 | return c_list[0] 155 | }else { 156 | return c_list[1] 157 | } 158 | }, 159 | ``` 160 | 161 | ### 3. 对于`is_absolute`的说明 162 | 163 | * `is_absolute`是指输出的html文件中的资源src/url,否使用绝对路径,默认值true,即启用绝对目录。 164 | 165 | * [常用]当使用服务器配置静态目录的情况下,推荐使用绝对目录。比如配合nginx,指定某个目录为静态资源目录。类似`/static/home/main.js`这种风格。 166 | 167 | * 如果无服务端情况下,有需要本地调试,推荐设置is_absolute为false, 即启用相对路径。类似`../../assets/static/home/main.js`这种风格。 168 | 169 | * 当is_absolute为false(启用相对路径)的情况下,直接打开输出的views目录下的html文件,就可以正常浏览、运行、调试 170 | 171 | 172 | 173 | ### 4. gulpman目录说明 174 | 175 | * 使用gulpman按照模块划分后,模块根目录可以是`./components`(默认,可配置),如果你有个模块是foo,那么应该有如下目录:`./components/foo`,然后跟foo模块相关的`html|js|css|fonts|image`等资源文件都放到`foo`下,这个结构下,做开发时非常清晰、高效,便于模块组织、资源定位等。 176 | 177 | * 通过`gm:develop`命令进入`develop`开发模式后,会自动生成模板`views`目录,和静态资源`assets`目录。 178 | 179 | * 通过`gm:publish`命令来构建发布资源,会自动生成生产环境下的模板目录`views_dist`,和静态资源目录`assets_dist`。 180 | 181 | 182 | ### 5. 什么是全局模块目录: 183 | 184 | - 对应`Browserify`的打包功能,`全局目录`是指可以直接`require`或者`import`其下的js模块的目录 185 | 186 | - `gulpman.config`的配置中,`lib`和`global`都是JS的全局模块目录。举个例子说明: 187 | * 你的`components/lib`目录下有一个模块 `foo.js`,就是: `components/lib/foo.js`,那么你在你的es6文件中,就可以这样使用:`import foo from 'foo'`,不需要写成 `import foo from '../lib/foo'` 188 | 189 | - 同理`global`那个配置也是这样的,推荐将lib目录设置成跟`bower`一致的,全部来存放第三方类库,而`global`设置的目录,比如叫`common`,可以存放自己的`公用模块`。这样开发会更加灵活、方便。 190 | 191 | - 注意全局模块不要有同名冲突。 192 | 193 | 194 | ### 6. 支持复杂目录和多级目录设定 195 | 196 | * 比如下面这种复杂路径: 197 | 198 | ```Javascript 199 | gulpman.config({ 200 | 'is_absolute': false, 201 | 'components': 'components/cc', 202 | 'runtime_views': 'runtime_views/rv', 203 | 'dist_views': 'dist_views/dv/dv', 204 | 205 | 'dist_assets': 'dist_assets/da', 206 | 'runtime_assets': 'runtime_assets/ra/ra', 207 | }) 208 | ``` 209 | 210 | 211 | ## Usage 使用 212 | 213 | ### 1. CLI 执行Task: 214 | 215 | ```Shell 216 | 217 | # 初始化目录,建立components目录并添加一份html的demo文件 218 | # init components dir and a html demo 219 | gulp gm:init 220 | 221 | 222 | # develop and watch 开发模式,监视相关文件变动,增量更新 223 | gulp gm:develop 224 | 225 | # 指定编译和监视某个component, 提高性能和效率(其他文件不编译输出) 226 | gulp gm:develop -c component_name1,component_name2,component_name3... 227 | 228 | 229 | # publish 发布资源,包括合并、压缩资源、rev产生MD5等 230 | gulp gm:publish 231 | 232 | # publish命令支持`-a`和`-v`参数指定输出资源/模板目录(可选) 233 | gulp gm:publish -v your_views_dist -a your_assets_dist 234 | 235 | # clean 清理构建输出的目录和文件 236 | gulp gm:clean 237 | 238 | # clean 清理构建输出的目录和文件,包括自文件夹/目录 239 | gulp gm:clean-deep 240 | 241 | # 编译输出一份运行时资源文件,但是不进入监视状态 242 | # compile for develop, not watch 243 | gulp gm:compile 244 | 245 | ``` 246 | 247 | 248 | ### 2. 开发中只监视某个component目录 249 | 250 | * 随着项目变大,开发中如果全局监视所有component资源,效率将会降低,因此可使用gulpman提供的监视子component的方式来开发,提高性能 251 | 252 | * 比如说,只监视components目录下的home模块: 253 | 254 | ```Shell 255 | # this will only watch `components/home` components 256 | gulp gm:develop -c home 257 | 258 | ``` 259 | 260 | 261 | ### 3. 如何在gulpman下使用React 262 | * 安装React: `npm install react react-dom` 263 | * 在ES6文件中使用 264 | 265 | ```Javascript 266 | import React from 'react'; 267 | import ReactDOM from 'react-dom'; 268 | 269 | // xxx 270 | ``` 271 | 272 | 273 | ### 4. 如何在js|es6|jsx中使用tpl模板 274 | 275 | * 目前支持.tpl扩展名的模板文件,直接打包到最终的js文件中 276 | 277 | * 用法:`import dialogTpl from './dialog.tpl'` 或者 `var dialogTpl = require('./dialog.tpl')` 278 | 279 | 280 | 281 | ### 5. 如何在HTML/CSS中嵌入base64编码的图片 282 | 283 | * 只需要图片资源后面添加`?_gm_inline`即可 284 | * 打包时候会将图片生成`base64`编码替换到到html中 285 | 286 | 287 | ##### html 288 | 289 | ```html 290 |

291 | Karat 克拉 292 |

293 | ``` 294 | 295 | ##### CSS/SCSS 296 | 297 | ```css 298 | 299 | .test { 300 | background: url(./img/testb64.png?_gm_inline) no-repeat; 301 | } 302 | ``` 303 | 304 | ### 6. 如何在HTML中嵌入内联CSS/JS 305 | 306 | * 类似图片base64,只需要资源后面添加`?_gm_inline`即可 307 | 308 | ```html 309 | 310 | 311 | 312 | ``` 313 | 314 | * 注:所有内嵌嵌入的资源,包括图片/JS/CSS,在develop(监视)模式下,都已自动关联更新。即如果a.html文件中,内联嵌入了一个b.css,如果b.css发生了修改,那么a.html会自动编译更新。 315 | 316 | 317 | ### 7. 如何使用Sprite雪碧图 318 | 319 | * 通过配置启用CSS Sprite `gulpman.config({ enableCSSSprite: true })` 默认false不启用 320 | * 基于spritesmith实现,在gulpman.config 中可传入spritesmith配置opts 321 | * 关于spritesmith详细参见:[https://github.com/Ensighten/spritesmith](https://github.com/Ensighten/spritesmith) 322 | * 只需要在scss文件中的图片url资源后面添加`?_gm_sprite`即可 323 | 324 | ```css 325 | .demo { 326 | background: url(./img/abc.png?_gm_sprite) no-repeat; 327 | 328 | /* other style you can set ...*/ 329 | width: 50px; 330 | height: 50px; 331 | } 332 | ``` 333 | 334 | ### 8. 如何使用Usemin 335 | 336 | * 整合usemin,在gulpman.config 中可传入usemin的配置opts 337 | * 关于usemin详细参见:[https://github.com/zont/gulp-usemin](https://github.com/zont/gulp-usemin) 338 | * 只需要在html文件中添加usemin的build注释即可。支持`js`|`css`|`inlinejs`|`inlinecss`等语法 339 | * 注意build注释中配置的输出路径写相对路径即可,跟script、link等标签类似,gulpman会自动转换成最终输出路径 340 | 341 | ```html 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | ``` 354 | 355 | 356 | ### 9. 如何使用前端js模板 357 | * 支持tpl扩展名,放到components相关目录下即可,js 可以直接require或者import 358 | * 最终会作为字符串格式打包进js 359 | * tpl中仍然支持资源嵌入和图片base64等,如参照前面_gm_inline等语法即可 360 | 361 | - import到es6中 362 | ```js 363 | import dialogTpl from './dialog.tpl' 364 | ``` 365 | 366 | - 或者使用require语法 367 | ```js 368 | var dialogTpl = require('./dialog.tpl') 369 | ``` 370 | 371 | ### 10. 引入css文件到js/html 372 | - 直接import即可,gulpman会自动加载到html页面中. 373 | ```js 374 | import from './style.css'; 375 | ``` 376 | 377 | ```js 378 | require('./style.css'); 379 | ``` 380 | 381 | 382 | ### 11. 使用iconfont转换 383 | * 可以将svg转换成icon-font,用`@font-face`方式引用 384 | * 初次使用先安装,运行`gulp gm:iconfont:install` 385 | * 将svg文件放到`components/iconfonts/source`目录下,运行`gulp gm:iconfont`即可 386 | * 自动生成的icon-font和css文件将会在`iconfonts/gmicon`目录下 387 | 388 | 389 | ### 12. 支持LAB.js来完成异步加载js 390 | * 引入LAB.js到项目中 391 | * 使用LAB的api来加载即可,使用相对路径 392 | * 代码用例: `$LAB.script("../testload/test.js").wait(()=>{console.log('test loaded')})` 393 | 394 | 395 | 396 | ### 13. Require CSS in js 397 | * 直接在js中require你的css文件(源文件时scss文件) 398 | * The CSS contents will be packaged into js files, and automatically injected to html when page opend. Using style tag 399 | * 使用时要确保添加 .css 扩展名 400 | * 举例: `require('./style.css')` or `import style from './style.css'` 401 | 402 | 403 | ### 14. 如何启用karma单元测试 404 | * 初次使用先安装,运行`gulp gm:karma:install`,会安装依赖和生成`karma.conf.js`文件 405 | * 在您的components中的对应模块目录下,建立一个spec文件夹,将对应的spec文件放在里面,文件拓展名是.es6 406 | * 运行 `gulp gm:karma:start` 来启动单元测试(watch模式),将会运行各spec文件,完成后可在生成的coverage文件夹中查看覆盖率结果 407 | * 指定spec目录、browsers等karma的选项,可以在`karma.conf.js`中设置、定制等 408 | 409 | 410 | 411 | ### 教程 412 | [浏览教程链接](http://karat.cc/article/56a351c3e48d2d05682aa0ac "karat.cc") 413 | 414 | ### License 415 | MIT 416 | -------------------------------------------------------------------------------- /assets/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015", "react", "stage-3"] 3 | } -------------------------------------------------------------------------------- /assets/build.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xunuoi/gulpman/d12eb0fa1c71a955f98d2c4bb7090540a518dd7a/assets/build.png -------------------------------------------------------------------------------- /assets/build.svg: -------------------------------------------------------------------------------- 1 | buildpassing -------------------------------------------------------------------------------- /assets/coverage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xunuoi/gulpman/d12eb0fa1c71a955f98d2c4bb7090540a518dd7a/assets/coverage.png -------------------------------------------------------------------------------- /assets/coverage.svg: -------------------------------------------------------------------------------- 1 | coverage100% -------------------------------------------------------------------------------- /assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xunuoi/gulpman/d12eb0fa1c71a955f98d2c4bb7090540a518dd7a/assets/logo.png -------------------------------------------------------------------------------- /karma/karma.conf.js: -------------------------------------------------------------------------------- 1 | var stringify = require('stringify'); 2 | 3 | var ignore_files = '**/lib/**', 4 | spec_files = '**/spec/**', 5 | tpl_files = '**/*.tpl', 6 | node_modules_files = '**/node_modules/**'; 7 | 8 | 9 | var babelOpts = { 10 | presets: ['es2015', 'react'], 11 | // sourceMap: 'inline', 12 | ignore: [node_modules_files, ignore_files, tpl_files] 13 | } 14 | 15 | module.exports = function(config) { 16 | config.set({ 17 | basePath: '', 18 | frameworks: [ 'browserify', 'jasmine' ], 19 | files: [ 20 | "./components/**/*.es6", 21 | ], 22 | browsers: ['Chrome'], 23 | preprocessors: { 24 | "./components/**/*.es6": ['browserify'] 25 | }, 26 | 27 | browserify: { 28 | debug: true, 29 | paths: ['./components/lib'], 30 | 31 | transform: [ 32 | stringify(['.tpl', '.txt']), 33 | 34 | ["babelify", babelOpts], 35 | 36 | ['browserify-istanbul', { 37 | instrumenter: require('babel-istanbul'), 38 | instrumenterConfig: { 39 | // embedSource: true, 40 | babel: babelOpts, 41 | }, 42 | ignore: [ 43 | spec_files, 44 | tpl_files, 45 | node_modules_files, 46 | ignore_files 47 | ], 48 | }] 49 | ], 50 | extensions: ['.es6', '.jsx', '.js'] 51 | }, 52 | // logLevel: config.LOG_DEBUG, 53 | reporters: ['progress', 'coverage'], 54 | coverageReporter: { 55 | type : 'html', 56 | dir : './coverage' 57 | }, 58 | // if true, Karma captures browsers, runs the tests and exits 59 | singleRun: false 60 | 61 | }); 62 | }; -------------------------------------------------------------------------------- /lib/assetsPathParser.js: -------------------------------------------------------------------------------- 1 | /** 2 | * For path replace into abs path 3 | */ 4 | 5 | 'use strict' 6 | 7 | 8 | var through = require('through2'), 9 | path = require('path'), 10 | j = path.join, 11 | 12 | gmutil = require('./gmutil') 13 | 14 | /* 15 | var ct = ';saasdfasf url()\n\r background: url ( "./img/1.png?_gm_inline&sd=32" ) no-repeat; \n\t; .d{ background-image: url(sss.png?_gm_sprite ) }'*/ 16 | 17 | var cssUrlReg = /url\s*\(\s*['"]?(.+?)['"]?\s*\)/gm 18 | 19 | 20 | var opts = {} 21 | 22 | 23 | function parseCSS(content, file){ 24 | var tList = content.match(cssUrlReg) 25 | 26 | if(tList){ 27 | tList.forEach(t=>{ 28 | 29 | var url = t.replace(/[\n\r\b\t\s'"]+/gm, '').replace(/^url\(/gm, '').replace(/\)$/gm, '') 30 | 31 | // 如果是相对路径,那么替换 32 | if(gmutil.isUrl('relative', url) ){ 33 | 34 | var baseDir = j(opts['cwd'], opts['static_dir']) 35 | 36 | // gmutil.alert('Base: '+baseDir) 37 | var fileRelativeDirOfComponents = path.relative(baseDir, file.path) 38 | 39 | 40 | // var hostFileAbsPath = j() 41 | var relativeDir = path.dirname(fileRelativeDirOfComponents) 42 | 43 | // gmutil.alert('relativeDir: '+relativeDir) 44 | 45 | var mediaRelPath = j(relativeDir, url) 46 | 47 | // gmutil.alert('mediaRelPath: '+mediaRelPath) 48 | 49 | var absUrl = j(opts['url_prefix'], mediaRelPath) 50 | 51 | // gmutil.alert('absUrl: '+absUrl) 52 | 53 | var tarStr = 'url('+absUrl+')' 54 | 55 | content = content.replace(t, tarStr) 56 | } 57 | 58 | }) 59 | } 60 | 61 | return content 62 | } 63 | 64 | 65 | function parseTpl(conf, contents, file){ 66 | 67 | let _opts = conf['_opts'], 68 | basepath = conf['basepath'], 69 | _isRuntimeDir = conf['_isRuntimeDir'], 70 | all_raw_source_reg = conf['all_raw_source_reg'], 71 | 72 | fdirname = path.dirname(file.relative), 73 | _urlPrefix // set assets url prefix 74 | 75 | 76 | if(_opts['is_absolute']) { 77 | _urlPrefix = _opts['url_prefix'] 78 | }else { 79 | // 判断打包资源中的url路径前缀 80 | let _fPath = j(_isRuntimeDir ? _opts['runtime_views'] : _opts['dist_views'], fdirname) 81 | 82 | let _staticPath = _isRuntimeDir ? _opts['runtime_static'] : _opts['dist_static'] 83 | 84 | _urlPrefix = path.relative(_fPath, _staticPath) 85 | } 86 | 87 | 88 | // 所有格式都要处理 89 | let srcQuoteReg = new RegExp('(?=[\'"]?)([\\w\\.\\-\\?\\-\\/\\:]+?(\\.('+all_raw_source_reg+')))(?=\\?_gm_inline)?(?=[\'"]?)', 'gm') 90 | // httpReg = /^http(s)?\:/ 91 | 92 | 93 | let tmp_rs_list = [], 94 | rs_list = [] 95 | 96 | // 提取单标签和双标签 97 | tmp_rs_list = tmp_rs_list 98 | .concat(contents.match(gmutil.reg['tagMedia'])) 99 | .concat(contents.match(gmutil.reg['closeTagMedia'])) 100 | 101 | // 首先提取标签,然后从标签中提取href或者src 102 | tmp_rs_list.length && ( 103 | rs_list = tmp_rs_list 104 | .filter(r=>(r && r.match(srcQuoteReg))) 105 | .map(v=>v.match(srcQuoteReg)[0]) 106 | .filter(r=>{ 107 | // remove the http:xxx.com/xx and base64 data url 108 | // 只处理相对路径 109 | // 不处理绝对路径、http、dataURL 110 | return gmutil.isUrl('relative', r) 111 | }) 112 | .filter(r=>!r.match(/^(['"]\/)/gm)) 113 | ) 114 | 115 | // 这里利用set做去重 116 | let rs_set = new Set(rs_list), 117 | srcPrefix = j(_urlPrefix, fdirname) 118 | 119 | // 替换url的的path和前缀 120 | rs_set.size && rs_set.forEach(epath=>{ 121 | // 对于base64的参数标识要保留,不能清理掉,因为后续要嵌入base64 122 | let innerReg = new RegExp('(?=[\'"]?)('+epath+')(\\?_gm_inline)*(?=[\'"]?)', 'gm') 123 | 124 | contents = contents.replace(innerReg, j(srcPrefix, epath)+'$2') 125 | }) 126 | 127 | // dealing with usemin build mark syntax 128 | // when in publish, not runtime-dir 129 | if(!_isRuntimeDir) { 130 | // 这里处理usemin 的build的注释内容 131 | contents = gmutil.replaceBuildBlock(contents, srcPrefix) 132 | } 133 | 134 | return contents 135 | } 136 | 137 | 138 | function parseJS(conf, contents, file){ 139 | 140 | let _opts = conf['_opts'], 141 | basepath = conf['basepath'], 142 | _isRuntimeDir = conf['_isRuntimeDir'], 143 | all_raw_source_reg = conf['all_raw_source_reg'], 144 | 145 | fdirname = path.dirname(file.relative), 146 | _urlPrefix // set assets url prefix 147 | 148 | if(_opts['is_absolute']) { 149 | _urlPrefix = _opts['url_prefix'] 150 | }else { 151 | // 判断打包资源中的url路径前缀 152 | let _fPath = j(_isRuntimeDir ? _opts['runtime_views'] : _opts['dist_views'], fdirname) 153 | 154 | let _staticPath = _isRuntimeDir ? _opts['runtime_static'] : _opts['dist_static'] 155 | 156 | _urlPrefix = path.relative(_fPath, _staticPath) 157 | } 158 | 159 | // for all src "xxx.yy" format path 160 | let srcQuoteReg = new RegExp('(?=[\'"]?)([\\w\\.\\-\\?\\-\\/\\:]+?(\\.('+all_raw_source_reg+')))(?=\\?_gm_inline)?(?=[\'"]?)', 'gm') 161 | // httpReg = /^http(s)?\:/ 162 | 163 | let tmp_rs_list = [], 164 | rs_list = [] 165 | 166 | // 提取单标签和双标签 167 | tmp_rs_list = tmp_rs_list 168 | .concat(contents.match(gmutil.reg['requireAsync'])) 169 | .concat(contents.match(gmutil.reg['jsAsyncLoad'])) 170 | 171 | // 首先提取标签,然后从标签中提取href或者src 172 | tmp_rs_list.length && ( 173 | rs_list = tmp_rs_list 174 | .filter(r=>(r && r.match(srcQuoteReg))) 175 | .map(v=>v.match(srcQuoteReg)[0]) 176 | .filter(r=>{ 177 | // remove the http:xxx.com/xx and base64 data url 178 | // 只处理相对路径 179 | // 不处理绝对路径、http、dataURL 180 | return gmutil.isUrl('relative', r) 181 | }) 182 | .filter(r=>!r.match(/^(['"]\/)/gm)) 183 | ) 184 | 185 | // 这里利用set做去重 186 | let rs_set = new Set(rs_list), 187 | srcPrefix = j(_urlPrefix, fdirname) 188 | 189 | // 替换url的的path和前缀 190 | rs_set.size && rs_set.forEach(epath=>{ 191 | // 对于base64的参数标识要保留,不能清理掉,因为后续要嵌入base64 192 | let innerReg = new RegExp('(?=[\'"]?)('+epath+')(\\?_gm_inline)*(?=[\'"]?)', 'gm') 193 | 194 | contents = contents.replace(innerReg, j(srcPrefix, epath)+'$2') 195 | }) 196 | 197 | // dealing with usemin build mark syntax 198 | // when in publish, not runtime-dir 199 | if(!_isRuntimeDir) { 200 | // 这里处理usemin 的build的注释内容 201 | contents = gmutil.replaceBuildBlock(contents, srcPrefix) 202 | } 203 | 204 | return contents 205 | } 206 | 207 | 208 | function _absolutizePath(_opts){ 209 | 210 | opts = Object.assign(opts, _opts) 211 | 212 | return through.obj(function(file, enc, cb){ 213 | 214 | if (file.isNull()) { 215 | this.push(file); 216 | return cb() 217 | } 218 | if (file.isStream()) { 219 | gmutil.error('*PathParser Error: Streaming not supported') 220 | return cb() 221 | } 222 | 223 | var fileType = path.extname(file.path).slice(1) 224 | 225 | if(opts['is_absolute']){ 226 | var content = file.contents.toString() 227 | 228 | if(fileType == 'css'){ 229 | var rc = parseCSS(content, file) 230 | }else if(fileType == 'tpl'){ 231 | var rc = parseTpl(opts, content, file) 232 | }else if(fileType == 'js' || fileType == 'es6'){ 233 | var rc = parseJS(opts, content, file) 234 | }else{ 235 | throw Error("Unknown Type: "+fileType) 236 | } 237 | 238 | file.contents = new Buffer(rc) 239 | 240 | } 241 | 242 | // gmutil.error(file.base) 243 | // gmutil.error(file.relative) 244 | // this will dest in right path 245 | if(fileType != 'js' && fileType != 'es6') { 246 | file.base = opts['cwd'] 247 | } 248 | 249 | this.push(file) 250 | return cb() 251 | }) 252 | } 253 | 254 | 255 | 256 | exports.absolutizePath = _absolutizePath 257 | -------------------------------------------------------------------------------- /lib/base64.js: -------------------------------------------------------------------------------- 1 | /** 2 | * FOR BASE 64 UTILS 3 | */ 4 | 5 | var path = require('path'), 6 | j = path.join, 7 | fs = require('fs'), 8 | gutil = require('gulp-util'), 9 | through = require('through2') 10 | 11 | 12 | var store = require('./store'), 13 | gmutil = require('./gmutil') 14 | 15 | 16 | var bError = null 17 | 18 | var opts 19 | 20 | const PLUGIN_NAME = 'Base64' 21 | 22 | 23 | function purifyUrlPrefix(str){ 24 | return str.replace(opts['url_prefix'], ''); 25 | } 26 | 27 | function purify(str){ 28 | return str.split('?')[0] 29 | // return str.replace(/\?_gm_inline/gm, '') 30 | } 31 | 32 | 33 | function parseSource(images, file, refType, content, baseDir){ 34 | 35 | var fileDir = path.dirname(file.relative) 36 | 37 | var status = true 38 | 39 | 40 | // gmutil.alert('debug: '+fileDir) 41 | 42 | images.forEach(function(item) { 43 | 44 | // remove url() in css 45 | var imageURL = item 46 | .replace(/\(|\)|\'/g, '') 47 | .replace(/^url/g, '') 48 | 49 | // 这里路经计算,需要后续优化 50 | // 现在相对路径计算繁琐了 51 | var pureFilePath; 52 | 53 | // console.log('item:', item) 54 | // // 如果url是相对路径,那么解析 55 | // if(!gmutil.isUrl('relative', imageURL)){ 56 | // // imageURL = imageURL.replace(opts['url_prefix'], '') 57 | // } 58 | 59 | if(refType == 'css'){ 60 | if(opts['isDevelop']){ 61 | file.base = opts['components'] 62 | fileDir = path.dirname(file.relative) 63 | } 64 | 65 | pureFilePath = j( 66 | file.cwd, 67 | baseDir, 68 | fileDir, 69 | imageURL 70 | ) 71 | 72 | // html or other source 73 | }else { 74 | 75 | var srcPath = purify(j(file.cwd, opts['views'], fileDir, imageURL)) 76 | 77 | // 输出资源在HMTL是相对路径情况下 78 | if(opts['is_absolute'] === false){ 79 | 80 | // develop模式下 81 | if(opts['isDevelop']){ 82 | 83 | pureFilePath = srcPath 84 | 85 | }else { 86 | // publish 模式下 87 | 88 | var dist_assets_path = j(file.cwd, opts['dist_assets']), 89 | runtime_assets_path = j(file.cwd, baseDir) 90 | 91 | pureFilePath = srcPath.replace(dist_assets_path, runtime_assets_path) 92 | 93 | } 94 | 95 | // 绝对路径情况下,比如/static/home/main.css 96 | }else { 97 | 98 | pureFilePath = j(baseDir, imageURL) 99 | } 100 | 101 | // remove url_prefix 102 | pureFilePath = purifyUrlPrefix(pureFilePath) 103 | 104 | } 105 | 106 | // 如果url是绝对路径,那么解析 107 | // }else { 108 | 109 | // } 110 | 111 | 112 | // remove base64 params 113 | pureFilePath = purify(pureFilePath) 114 | // gmutil.warn('pureFilePath: \n\n\n'+pureFilePath+'\n\n') 115 | 116 | // check if exist 117 | try { 118 | var filepath = fs.realpathSync(pureFilePath); 119 | }catch(err){ 120 | 121 | bError = err 122 | 123 | gmutil.error(err['message']) 124 | gmutil.error('\n*Check Your Component: '+fileDir) 125 | 126 | status = false 127 | 128 | return false 129 | } 130 | 131 | var extname = purify(path.extname(imageURL).slice(1)) 132 | 133 | var imageContent = new Buffer(fs.readFileSync(filepath)).toString('base64') 134 | 135 | content = content.replace( 136 | item, 137 | 'data:image/' + 138 | extname.toLowerCase() + 139 | ';base64,' + 140 | imageContent 141 | ) 142 | 143 | // generate json file of relevance 144 | store.save(filepath, gmutil.convertSource(file.path, refType)) 145 | gmutil.log('*Convert to Base64: ' + filepath, 'green') 146 | }) 147 | 148 | 149 | if(status) { 150 | return content 151 | }else { 152 | return false 153 | } 154 | } 155 | 156 | 157 | 158 | function toBase64(_opts) { 159 | opts = _opts || {} 160 | 161 | var rule = opts.rule || /url\([^\)]+\)/g 162 | 163 | var refType = opts.type || 'css', 164 | baseDir = opts.baseDir || './' 165 | 166 | opts['dist_assets'] || (opts['dist_assets'] = '') 167 | 168 | return through.obj(function (file, enc, cb) { 169 | if (file.isNull()) { 170 | this.push(file) 171 | return cb() 172 | } 173 | 174 | if (file.isStream()) { 175 | this.emit('error', new gutil.PluginError(PLUGIN_NAME, 'Streaming not supported')) 176 | return cb() 177 | } 178 | 179 | var content = file.contents.toString() 180 | var images = content.match(rule) 181 | 182 | if(images){ 183 | content = parseSource(images, file, refType, content, baseDir, this) 184 | 185 | // 如果出现错误返回了false 186 | if(content === false){ 187 | var errMes = 'File Not Exist' 188 | 189 | if(bError){ 190 | errMes = bError['message'] 191 | } 192 | 193 | this.emit('error', new gutil.PluginError(PLUGIN_NAME, errMes)) 194 | }else { 195 | file.contents = new Buffer(content) 196 | } 197 | 198 | 199 | } 200 | 201 | this.push(file) 202 | cb() 203 | }) 204 | } 205 | 206 | module.exports = toBase64; -------------------------------------------------------------------------------- /lib/cdnProxy.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * FOR CDN PREFIX CONFIG 4 | * 5 | * FIX gulp-rev-all and revReplace about `prefix` 6 | */ 7 | 8 | /** 9 | * Another Proxy code in revReplace.js, 10 | * include random url of list and function 11 | */ 12 | 13 | 14 | var gmutil = require('./gmutil') 15 | 16 | 17 | function proxyPrefix(obj){ 18 | // fix prefix options for gulp-rev-all 19 | // 注意: 这了可能会干预到rev-replace里的变量 20 | 21 | obj['cdn_prefix'] = obj['cdn_prefix'] || '' 22 | obj['_prefix'] = obj['cdn_prefix'] 23 | 24 | 25 | // fix array 26 | if(obj['cdn_prefix'] instanceof Array){ 27 | 28 | // console.log('Array: \n', obj['cdn_prefix']) 29 | 30 | function _getRandPrefixFromList(){ 31 | return obj['_prefix'][gmutil.randomNum(0, obj['_prefix'].length-1)] 32 | } 33 | 34 | // rewrite replace of array 35 | obj['cdn_prefix'].replace = function(a, b){ 36 | 37 | var p = _getRandPrefixFromList().replace(a, b) 38 | 39 | return p 40 | } 41 | 42 | // rewrite toString of array 43 | obj['cdn_prefix'].toString = function(){ 44 | 45 | return _getRandPrefixFromList() 46 | } 47 | 48 | 49 | }else if(obj['cdn_prefix'] instanceof Function){ 50 | 51 | // !! only for gulp-rev-all 52 | obj['transformPath'] = function (rev, source, file) { 53 | 54 | return obj['_prefix'](source, rev, file)+rev 55 | 56 | // on the remote server, image files are served from `/images` 57 | // return rev.replace('/img', '/images') 58 | } 59 | 60 | 61 | }else if(typeof obj['cdn_prefix'] == 'string'){ 62 | 63 | // no handle 64 | // obj['_all_prefix'].add(obj['cdn_prefix']) 65 | 66 | }else { 67 | throw Error('Unknown Type: '+ (typeof obj['cdn_prefix'])) 68 | } 69 | 70 | 71 | return obj 72 | 73 | } 74 | 75 | 76 | exports.proxyPrefix = proxyPrefix -------------------------------------------------------------------------------- /lib/gmutil.js: -------------------------------------------------------------------------------- 1 | /** 2 | * FOR UTILS 3 | */ 4 | 5 | 'use strict' 6 | 7 | 8 | var colors = require('colors'), 9 | path = require('path'), 10 | j = path.join 11 | 12 | 13 | function _log (str, color) { 14 | 15 | str += '' 16 | 17 | var color = color || 'blue' 18 | 19 | console.log(str[color]) 20 | } 21 | 22 | function _error (str){ 23 | 24 | return _log(str, 'red') 25 | } 26 | 27 | function _alert(str) { 28 | return _log(str, 'red') 29 | } 30 | 31 | function _tip (str){ 32 | 33 | return _log(str, 'green') 34 | } 35 | 36 | function _warn (str){ 37 | 38 | return _log(str, 'yellow') 39 | } 40 | 41 | 42 | /** 43 | * Returns a random number between min (inclusive) and max (exclusive) 44 | */ 45 | function getRandomArbitrary(min, max) { 46 | return Math.random() * (max - min) + min; 47 | } 48 | 49 | /** 50 | * Returns a random integer between min (inclusive) and max (inclusive) 51 | * Using Math.round() will give you a non-uniform distribution! 52 | */ 53 | function randomNum(min, max) { 54 | return Math.floor(Math.random() * (max - min + 1)) + min; 55 | } 56 | 57 | 58 | function byLongestUnreved(a, b) { 59 | return b.unreved.length - a.unreved.length; 60 | } 61 | 62 | 63 | function pathInAssets(_cwd, fpath, componentsDir, runStaticDir){ 64 | var componentsAbsPath = j(_cwd, componentsDir), 65 | imgRelPath = fpath.replace(componentsAbsPath, ''), 66 | imgAbsPath = j(_cwd, runStaticDir, imgRelPath) 67 | 68 | return imgAbsPath 69 | } 70 | 71 | 72 | function validateObj(obj){ 73 | var a 74 | for(a in obj){ 75 | if(obj.hasOwnProperty(a) && obj[a] === undefined) { 76 | delete obj[a] 77 | } 78 | } 79 | 80 | return obj 81 | } 82 | 83 | function isArray(o) { 84 | return Object.prototype.toString.call(o) === '[object Array]'; 85 | } 86 | 87 | 88 | function convertSource(rawSource, refType){ 89 | let tarType 90 | 91 | if(refType == 'css'){ 92 | tarType = 'scss' 93 | } 94 | 95 | var repReg = new RegExp('\\.'+refType+'$', 'g'), 96 | rsList = [] 97 | 98 | if(!rawSource.match(repReg)) { 99 | 100 | return rawSource 101 | 102 | } 103 | 104 | if(typeof tarType == 'string'){ 105 | return rawSource.replace(repReg, '.'+tarType) 106 | 107 | }else { 108 | return rawSource 109 | } 110 | 111 | } 112 | 113 | 114 | function isUrl(urlType, str){ 115 | var _d = { 116 | 'absolute': /^\//g, 117 | 'http': /^http(s)?\:/g, 118 | 'dataURL': /^data\:/g, 119 | } 120 | 121 | var t = _d[urlType] 122 | 123 | if(urlType == 'relative'){ 124 | return !isUrl('absolute', str) && 125 | !isUrl('http', str) && 126 | !isUrl('dataURL', str) 127 | } 128 | 129 | if(!t){ 130 | throw Error('Unknow Type: '+t) 131 | } 132 | 133 | return str.match(_d[urlType]) 134 | } 135 | 136 | function joinUrl (a, b){ 137 | if(!b.match(/\/$/g)) { 138 | b = b + '/' 139 | } 140 | 141 | if(!a.match(/\/$/g) && !b.match(/^\//g)) { 142 | return a + '/' + b; 143 | }else if(a.match(/\/$/g) && b.match(/^\//g)){ 144 | 145 | return a.replace(/\/$/g, '') + b; 146 | }else{ 147 | return a + b; 148 | } 149 | 150 | } 151 | 152 | 153 | function replaceBuildBlock(content, srcPrefix) { 154 | 155 | var startReg = //gim; 156 | var endReg = //gim; 157 | 158 | 159 | var sections = content.split(endReg) 160 | 161 | sections.forEach((e, i)=>{ 162 | var block 163 | 164 | if( (block = e.match(startReg)) && (block = block[0])){ 165 | var section = e.split(startReg) 166 | // var block = e.match(startReg)[0] 167 | var outputPathParam = section[4]; 168 | if(!outputPathParam) { 169 | // not set output path in usemin, build:js/css 170 | var srcStr = section[5] 171 | var doExec = true 172 | var comboNameList = [] 173 | var comboExtFileName 174 | 175 | while(doExec) { 176 | let oneFile = _reg['sourceUrl'].exec(srcStr) 177 | let ofname 178 | if(oneFile && (ofname = oneFile[2])){ 179 | let oneFileName = path.basename(ofname); 180 | let extFileName = path.extname(ofname) 181 | if(!comboExtFileName) { 182 | comboExtFileName = extFileName 183 | } 184 | 185 | comboNameList.push(oneFileName.replace(extFileName, '')) 186 | }else { 187 | doExec = false 188 | } 189 | } 190 | if(!comboExtFileName) { 191 | comboExtFileName = '.unknown' 192 | _warn('*Found Unknown Extname, check your assets files!') 193 | } 194 | // add 80 as max combo name length 195 | var comboNameStr = comboNameList.join('_').slice(0, 80) + comboExtFileName 196 | var outputPathParam = comboNameStr.split('?')[0] 197 | _warn('*Not set build file name, use combo name: ' + outputPathParam) 198 | // _error(outputPathParam) 199 | } 200 | var newOutPath = j(srcPrefix, outputPathParam) 201 | 202 | // _alert('Usemin Build: '+newOutPath) 203 | 204 | // section[2]: alternative search path 205 | // section[3]: nameInHTML: section[3] 206 | // section[4]: relative out path 207 | 208 | content = content.replace(block, '') 209 | 210 | } 211 | }) 212 | 213 | 214 | return content 215 | 216 | } 217 | 218 | 219 | // source reg 220 | var _reg = { 221 | link: new RegExp('[\\s\\S]*?<*\\/*>*', 'gi'), 222 | img: new RegExp('[\\s\\S]*?<*\\/*>*', 'gi'), 223 | 224 | href: new RegExp('\\s*(href)=["\']+([\\s\\S]*?)["\']'), 225 | style:new RegExp('[\\s\\S]*?<\\/style>', 'gi'), 226 | script: new RegExp('[\\s\\S]*?<\\/script>', 'gi'), 227 | src: new RegExp('\\s*(src)=["\']+([\\s\\S]*?)["\']'), 228 | 229 | sourceUrl: new RegExp('\\s*(src|href)=["\']+([\\s\\S]*?)["\']', 'gi'), 230 | 231 | jsAsyncLoad: new RegExp('\\s*(\\.script\\()["\']+([\\s\\S]*?)["\']\\)', 'gi'), 232 | requireAsync: new RegExp('\\s*(require\\.async\\()["\']+([\\s\\S]*?)["\']\\)', 'gi'), 233 | 234 | tagMedia: new RegExp('<(img|link|source|input)\\s+[\\s\\S]*?>[\\s\\S]*?<*\\/*>*', 'gi'), 235 | 236 | closeTagMedia: new RegExp('<(script|iframe|frame|audio|video|object)\\s*[\\s\\S]*?>[\\s\\S]*?<\\/(script|iframe|frame|audio|video|object)>', 'gi'), 237 | 238 | useminBuild: new RegExp('<\\!\\-\\-\\s+build\\:\\w+\\s+([\\w\\.\\/\\-\\?\\=&]+)\\s+\\-\\->', 'gm') 239 | 240 | } 241 | 242 | // log 243 | exports.log = _log 244 | exports.error = _error 245 | exports.alert = _alert 246 | exports.tip = _tip 247 | exports.warn = _warn 248 | 249 | exports.reg = _reg 250 | 251 | // for rev replace 252 | exports.byLongestUnreved = byLongestUnreved 253 | exports.joinUrl = joinUrl 254 | // utils 255 | exports.randomNum = randomNum 256 | exports.isArray = isArray 257 | exports.pathInAssets = pathInAssets 258 | exports.validateObj = validateObj 259 | exports.convertSource = convertSource 260 | 261 | // for usemin build 262 | exports.replaceBuildBlock = replaceBuildBlock 263 | 264 | exports.isUrl = isUrl 265 | -------------------------------------------------------------------------------- /lib/gulp-usemin/.npmignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /*.log 3 | .idea/ 4 | *~ 5 | -------------------------------------------------------------------------------- /lib/gulp-usemin/.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "4.0" 4 | before_script: 5 | - npm install 6 | -------------------------------------------------------------------------------- /lib/gulp-usemin/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Alexander Zonov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | 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, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /lib/gulp-usemin/README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/zont/gulp-usemin.svg?branch=master)](https://travis-ci.org/zont/gulp-usemin) 2 | 3 | # gulp-usemin 4 | > Replaces references to non-optimized scripts or stylesheets into a set of HTML files (or any templates/views). 5 | 6 | This task is designed for gulp >= 3 and node >= 4.0. 7 | > Attention: v0.3.0 options is not compatible with v0.2.0. 8 | 9 | ## Usage 10 | 11 | First, install `gulp-usemin` as a development dependency: 12 | 13 | ```shell 14 | npm install --save-dev gulp-usemin 15 | ``` 16 | 17 | Then, add it to your `gulpfile.js`: 18 | 19 | ```javascript 20 | var usemin = require('gulp-usemin'); 21 | var uglify = require('gulp-uglify'); 22 | var minifyHtml = require('gulp-minify-html'); 23 | var minifyCss = require('gulp-minify-css'); 24 | var rev = require('gulp-rev'); 25 | 26 | 27 | gulp.task('usemin', function() { 28 | return gulp.src('./*.html') 29 | .pipe(usemin({ 30 | css: [ rev() ], 31 | html: [ minifyHtml({ empty: true }) ], 32 | js: [ uglify(), rev() ], 33 | inlinejs: [ uglify() ], 34 | inlinecss: [ minifyCss(), 'concat' ] 35 | })) 36 | .pipe(gulp.dest('build/')); 37 | }); 38 | ``` 39 | 40 | If you need to call the same pipeline twice, you need to define each task as a function that returns the stream object that should be used. 41 | 42 | ```javascript 43 | gulp.task('usemin', function() { 44 | return gulp.src('./*.html') 45 | .pipe(usemin({ 46 | css: [ rev ], 47 | html: [ function () {return minifyHtml({ empty: true });} ], 48 | js: [ uglify, rev ], 49 | inlinejs: [ uglify ], 50 | inlinecss: [ minifyCss, 'concat' ] 51 | })) 52 | .pipe(gulp.dest('build/')); 53 | }); 54 | ``` 55 | 56 | 57 | ## API 58 | 59 | ### Blocks 60 | Blocks are expressed as: 61 | 62 | ```html 63 | 64 | ... HTML Markup, list of script / link tags. 65 | 66 | ``` 67 | 68 | - **pipelineId**: pipeline id for options or *remove* to remove a section 69 | - **alternate search path**: (optional) By default the input files are relative to the treated file. Alternate search path allows one to change that 70 | - **path**: the file path of the optimized file, the target output 71 | 72 | An example of this in completed form can be seen below: 73 | 74 | ```html 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | ``` 111 | 112 | ### Options 113 | 114 | #### assetsDir 115 | Type: `String` 116 | 117 | Alternate root path for assets. New concated js and css files will be written to the path specified in the build block, relative to this path. Currently asset files are also returned in the stream. 118 | 119 | #### path 120 | Type: `String` 121 | 122 | Default alternate search path for files. Can be overridden by the alternate search path option for a given block. 123 | 124 | #### any pipelineId 125 | Type: `Array` 126 | 127 | If exist used for modify files. If does not contain string 'concat', then it added as first member of pipeline 128 | 129 | #### outputRelativePath 130 | Type: `String` 131 | Relative location to html file for new concatenated js and css. 132 | 133 | #### enableHtmlComment 134 | Type: `Boolean` 135 | 136 | Keep HTML comment when processing 137 | 138 | #### jsAttributes 139 | Type: `Object` 140 | 141 | Attach HTML attributes to the output js file. 142 | For Example : 143 | ```js 144 | gulp.task('usemin', function() { 145 | return gulp.src('./index.html') 146 | .pipe(usemin({ 147 | html: [], 148 | jsAttributes : { 149 | async : true, 150 | lorem : 'ipsum', 151 | seq : [1, 2, 1] 152 | }, 153 | js: [ ], 154 | js1:[ ], 155 | js2:[ ] 156 | })) 157 | .pipe(gulp.dest('./')); 158 | }); 159 | ``` 160 | Will give you : 161 | ```html 162 | 163 | 164 | 165 | ``` 166 | As your built script tag. 167 | 168 | ## Use case 169 | 170 | ``` 171 | | 172 | +- app 173 | | +- index.html 174 | | +- assets 175 | | +- js 176 | | +- foo.js 177 | | +- bar.js 178 | | +- css 179 | | +- clear.css 180 | | +- main.css 181 | +- dist 182 | ``` 183 | 184 | We want to optimize `foo.js` and `bar.js` into `optimized.js`, referenced using relative path. `index.html` should contain the following block: 185 | 186 | ``` 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | ``` 197 | 198 | We want our files to be generated in the `dist` directory. `gulpfile.js` should contain the following block: 199 | 200 | ```javascript 201 | gulp.task('usemin', function () { 202 | return gulp.src('./app/index.html') 203 | .pipe(usemin({ 204 | js: [uglify()] 205 | // in this case css will be only concatenated (like css: ['concat']). 206 | })) 207 | .pipe(gulp.dest('dist/')); 208 | }); 209 | ``` 210 | 211 | This will generate the following output: 212 | 213 | ``` 214 | | 215 | +- app 216 | | +- index.html 217 | | +- assets 218 | | +- js 219 | | +- foo.js 220 | | +- bar.js 221 | +- dist 222 | | +- index.html 223 | | +- js 224 | | +- optimized.js 225 | | +- style.css 226 | ``` 227 | 228 | `index.html` output: 229 | 230 | ``` 231 | 232 | 233 | 234 | ``` 235 | 236 | ## Changelog 237 | 238 | #####0.3.23 239 | - Added support array value for cssAttributes (by MillerRen) 240 | 241 | #####0.3.22 242 | - Added html import support (by linfaxin) 243 | 244 | #####0.3.21 245 | - Added support paths with querystring or hash (by Lanfei) 246 | 247 | #####0.3.20 248 | - Added support array value for jsAttributes (by kuitos) 249 | 250 | #####0.3.18 251 | - Fixed relative path for script in subfolder bug 252 | 253 | #####0.3.17 254 | - Fixed block output when stream returns multiple files (by maksidom) 255 | 256 | #####0.3.16 257 | - Added feature to assign attributes to js script tags (by sohamkamani) 258 | 259 | #####0.3.15 260 | - Allow proper html output when blocks are empty (by ppowalowski) 261 | 262 | #####0.3.14 263 | - fixed #91 264 | 265 | #####0.3.13 266 | - works fine only with gulp-foreach 267 | 268 | #####0.3.12 269 | - fixed #121. Depending on the node >= 0.12. 270 | 271 | #####0.3.11 272 | - fixed #88 273 | 274 | #####0.3.10 275 | - fixed uppercase Q bug (on case-sensetive file systems) 276 | 277 | #####0.3.9 278 | - async tasks support 279 | 280 | #####0.3.8 281 | - allow removal option (by tejohnso) 282 | - added support for single quotes (by adicirstei) 283 | 284 | #####0.3.7 285 | - ouputRelativePath renamed outputRelativePath 286 | 287 | #####0.3.6 288 | - ouputRelativePath option (by bhstahl) 289 | 290 | #####0.3.5 291 | - Support for conditional comments inside build blocks (by simplydenis) 292 | 293 | #####0.3.4 294 | - When a file does not exist an error containing the missing path is thrown 295 | 296 | #####0.3.3 297 | - fixed dependencies 298 | - Add support for multiple alternative paths (by peleteiro) 299 | 300 | #####0.3.2 301 | - fixed assetsDir option (by rovjuvano) 302 | 303 | #####0.3.1 304 | - fixed fails to create source map files by uglify({outSourceMap: true}) 305 | 306 | #####0.3.0 307 | - new version of options 308 | 309 | #####0.2.3 310 | - fixed html minify bug 311 | 312 | #####0.2.2 313 | - allow gulp-usemin to work with minified source HTML (by CWSpear) 314 | - fixed alternate path bug (by CWSpear) 315 | - add assetsDir option (by pursual) 316 | - add rev option (by pursual) 317 | 318 | #####0.2.1 319 | - fixed subfolders bug 320 | 321 | #####0.2.0 322 | - no minification by default. New options API 323 | 324 | #####0.1.4 325 | - add alternate search path support 326 | 327 | #####0.1.3 328 | - add support for absolute URLs (by vasa-chi) 329 | 330 | #####0.1.1 331 | - fixed aggressive replace comments 332 | 333 | #####0.1.0 334 | - fixed some bugs. Add tests. 335 | 336 | #####0.0.2 337 | - add minification by default 338 | 339 | #####0.0.1 340 | - initial release 341 | -------------------------------------------------------------------------------- /lib/gulp-usemin/gulpfile.js: -------------------------------------------------------------------------------- 1 | /* jshint node:true */ 2 | 3 | 'use strict'; 4 | 5 | var gulp = require('gulp'); 6 | var jshint = require('gulp-jshint'); 7 | var mocha = require('gulp-mocha'); 8 | 9 | gulp.task('lint', function() { 10 | return gulp.src('test/main.js') 11 | .pipe(jshint()) 12 | .pipe(jshint.reporter('default')) 13 | .pipe(mocha()); 14 | }); 15 | 16 | gulp.task('default', ['lint']); -------------------------------------------------------------------------------- /lib/gulp-usemin/index.js: -------------------------------------------------------------------------------- 1 | module.exports = function(options) { 2 | var through = require('through2'); 3 | var gutil = require('gulp-util'); 4 | var blocksBuilder = require('./lib/blocksBuilder.js'); 5 | var htmlBuilder = require('./lib/htmlBuilder.js'); 6 | 7 | return through.obj(function(file, enc, callback) { 8 | if (file.isStream()) { 9 | this.emit('error', new gutil.PluginError('gulp-usemin', 'Streams are not supported!')); 10 | callback(); 11 | } 12 | else if (file.isNull()) 13 | callback(null, file); // Do nothing if no contents 14 | else { 15 | try { 16 | var blocks = blocksBuilder(file, options); 17 | htmlBuilder(file, blocks, options, this.push.bind(this), callback); 18 | } catch(e) { 19 | this.emit('error', e); 20 | callback(); 21 | } 22 | } 23 | }); 24 | }; 25 | -------------------------------------------------------------------------------- /lib/gulp-usemin/lib/blocksBuilder.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | var glob = require('globby'); 3 | var path = require('path'); 4 | var gutil = require('gulp-util'); 5 | 6 | module.exports = function(file, options) { 7 | options = options || {}; 8 | 9 | var startReg = //gim; 10 | var endReg = //gim; 11 | var jsReg = /<\s*script\s+.*?src\s*=\s*['"]?([^'"?# ]+).*?><\s*\/\s*script\s*>/gi; 12 | var cssReg = /<\s*link\s+.*?href\s*=\s*['"]?([^'"?# ]+).*?>/gi; 13 | var cssMediaReg = /<\s*link\s+.*?media\s*=\s*['"]?([^'" ]+).*?>/gi; 14 | var startCondReg = //gim; 16 | 17 | var basePath = file.base; 18 | var mainPath = path.dirname(file.path); 19 | var outputPath = options.outputRelativePath || ''; 20 | var content = String(file.contents); 21 | var sections = content.split(endReg); 22 | var blocks = []; 23 | var cssMediaQuery = null; 24 | 25 | function getFiles(content, reg, alternatePath) { 26 | var paths = []; 27 | var files = []; 28 | cssMediaQuery = null; 29 | 30 | content 31 | .replace(startCondReg, '') 32 | .replace(endCondReg, '') 33 | .replace(//gim, function (a) { 34 | return options.enableHtmlComment ? a : ''; 35 | }) 36 | .replace(reg, function (a, b) { 37 | var tmpfileRelPath = b.replace(/^'|^"/, '').replace(/'$/, '').replace(/"$/, ''); 38 | 39 | if(options['url_prefix']){ 40 | var fileRelativePath = path.relative(options['url_prefix'], tmpfileRelPath); 41 | }else { 42 | var fileRelativePath = tmpfileRelPath; 43 | } 44 | 45 | var filePath = path.resolve(path.join( 46 | alternatePath || options.path || mainPath, 47 | fileRelativePath 48 | )); 49 | if (options.assetsDir) 50 | filePath = path.resolve(path.join(options.assetsDir, path.relative(basePath, filePath))); 51 | 52 | paths.push(filePath); 53 | }); 54 | 55 | if (reg === cssReg) { 56 | content.replace(cssMediaReg, function(a, media) { 57 | media = media.replace(/^'|^"/, '').replace(/'$/, '').replace(/"$/, ''); 58 | 59 | if (!cssMediaQuery) { 60 | cssMediaQuery = media; 61 | } else { 62 | if (cssMediaQuery != media) 63 | throw new gutil.PluginError('gulp-usemin', 'incompatible css media query for ' + a + ' detected.'); 64 | } 65 | }); 66 | } 67 | 68 | for (var i = 0, l = paths.length; i < l; ++i) { 69 | var filepaths = glob.sync(paths[i]); 70 | if(filepaths[0] === undefined) { 71 | throw new gutil.PluginError('gulp-usemin', 'Path ' + paths[i] + ' not found!'); 72 | } 73 | filepaths.forEach(function (filepath) { 74 | files.push(new gutil.File({ 75 | path: filepath, 76 | contents: fs.readFileSync(filepath) 77 | })); 78 | }); 79 | } 80 | 81 | return files; 82 | } 83 | 84 | for (var i = 0, l = sections.length; i < l; ++i) { 85 | if (sections[i].match(startReg)) { 86 | var section = sections[i].split(startReg); 87 | blocks.push(section[0]); 88 | 89 | var startCondLine = section[5].match(startCondReg); 90 | var endCondLine = section[5].match(endCondReg); 91 | if (startCondLine && endCondLine) 92 | blocks.push(startCondLine[0]); 93 | 94 | if (section[1] !== 'remove') { 95 | if(section[1] === 'htmlimport'){ 96 | blocks.push({ 97 | type: 'htmlimport', 98 | nameInHTML: section[3], 99 | name: path.join(outputPath || path.relative(basePath, mainPath), section[4]), 100 | files: getFiles(section[5], cssReg, section[2]), 101 | tasks: options[section[1]] 102 | }); 103 | 104 | }else if (jsReg.test(section[5])) { 105 | if (section[1].indexOf('inline') !== -1) { 106 | blocks.push({ 107 | type: 'inlinejs', 108 | files: getFiles(section[5], jsReg, section[2]), 109 | tasks: options[section[1]] 110 | }); 111 | } 112 | else { 113 | blocks.push({ 114 | type: 'js', 115 | nameInHTML: section[3], 116 | name: path.join(outputPath || path.relative(basePath, mainPath), section[4]), 117 | files: getFiles(section[5], jsReg, section[2]), 118 | tasks: options[section[1]] 119 | }); 120 | } 121 | 122 | } 123 | else { 124 | if (section[1].indexOf('inline') !== -1) { 125 | blocks.push({ 126 | type: 'inlinecss', 127 | files: getFiles(section[5], cssReg, section[2]), 128 | tasks: options[section[1]], 129 | mediaQuery: cssMediaQuery 130 | }); 131 | } 132 | else { 133 | blocks.push({ 134 | type: 'css', 135 | nameInHTML: section[3], 136 | name: path.join(outputPath || path.relative(basePath, mainPath), section[4]), 137 | files: getFiles(section[5], cssReg, section[2]), 138 | tasks: options[section[1]], 139 | mediaQuery: cssMediaQuery 140 | }); 141 | } 142 | } 143 | } 144 | 145 | if (startCondLine && endCondLine) 146 | blocks.push(endCondLine[0]); 147 | } else 148 | blocks.push(sections[i]); 149 | } 150 | 151 | return blocks; 152 | }; 153 | -------------------------------------------------------------------------------- /lib/gulp-usemin/lib/htmlBuilder.js: -------------------------------------------------------------------------------- 1 | module.exports = function(file, blocks, options, push, callback) { 2 | var path = require('path'); 3 | var gutil = require('gulp-util'); 4 | var pipeline = require('./pipeline.js'); 5 | 6 | var basePath = file.base; 7 | var name = path.basename(file.path); 8 | var mainPath = path.dirname(file.path); 9 | 10 | function createFile(name, content) { 11 | var filePath = path.join(path.relative(basePath, mainPath), name); 12 | return new gutil.File({ 13 | path: filePath, 14 | contents: new Buffer(content) 15 | }) 16 | } 17 | 18 | function createHTMLAttributes(attributes, index){ 19 | if(!attributes){ 20 | return ''; 21 | } 22 | var attrArray = []; 23 | Object.keys(attributes).forEach(function (attribute) { 24 | 25 | var attributeValue = attributes[attribute]; 26 | 27 | if (attributeValue === true) { 28 | attrArray.push(attribute); 29 | return; 30 | } 31 | if (attributeValue === false) { 32 | return; 33 | } 34 | 35 | if (Array.isArray(attributeValue)) { 36 | attrArray.push(attribute + '="' + attributeValue[index] + '"'); 37 | } else { 38 | attrArray.push(attribute + '="' + attributeValue + '"'); 39 | } 40 | 41 | }); 42 | return ' ' + attrArray.join(' '); 43 | } 44 | 45 | // fix for revReplace url, if not fix then get: /http://s.cdn.com/public/a/b.js 46 | function _fixUrlForRevReplace(str){ 47 | return str; 48 | // return str ? str.replace(/^\//g, '') : str; 49 | } 50 | 51 | function _fixFilePath(name, file){ 52 | var realRelativeFilePath = '/' + path.relative(options['url_prefix'], name); 53 | 54 | file.path = file.path.replace(name, realRelativeFilePath); 55 | return file; 56 | } 57 | 58 | var html = []; 59 | var jsCounter = 0; 60 | var cssCounter = 0; 61 | var promises = blocks.map(function(block, i) { 62 | return new Promise(function(resolve) { 63 | html[i] = ''; 64 | if (typeof block == 'string') { 65 | html[i] = block; 66 | resolve(); 67 | } 68 | else if (block.files.length == 0){ 69 | resolve(); 70 | } 71 | else if (block.type == 'js') { 72 | pipeline(block.name, block.files, block.tasks, function(name, file) { 73 | _fixFilePath(name, file); 74 | push(file); 75 | 76 | var jsAttributes = options ? options.jsAttributes : null; 77 | if (path.extname(file.path) == '.js') 78 | html[i] += ''; 79 | resolve(); 80 | }.bind(this, block.nameInHTML)); 81 | } 82 | else if (block.type == 'css') { 83 | pipeline(block.name, block.files, block.tasks, function(name, file) { 84 | _fixFilePath(name, file); 85 | push(file); 86 | var cssAttributes = options ? options.cssAttributes : null; 87 | html[i] += ''; 89 | resolve(); 90 | }.bind(this, block.nameInHTML)); 91 | } 92 | else if (block.type == 'inlinejs') { 93 | pipeline(block.name, block.files, block.tasks, function(file) { 94 | html[i] = ''; 95 | resolve(); 96 | }.bind(this)); 97 | } 98 | else if (block.type == 'inlinecss') { 99 | pipeline(block.name, block.files, block.tasks, function(file) { 100 | html[i] = '' 101 | + String(file.contents) + ''; 102 | resolve(); 103 | }.bind(this)); 104 | } 105 | else if (block.type == 'htmlimport') { 106 | pipeline(block.name, block.files, block.tasks, function(name, file) { 107 | _fixFilePath(name, file); 108 | push(file); 109 | html[i] += ''; 110 | resolve(); 111 | }.bind(this, block.nameInHTML)); 112 | } 113 | }); 114 | }); 115 | 116 | Promise.all(promises).then(function() { 117 | var createdFile = createFile(name, html.join('')); 118 | pipeline(createdFile.path, [createdFile], options && options['html'], function(file) { 119 | callback(null, file); 120 | }); 121 | }); 122 | }; 123 | -------------------------------------------------------------------------------- /lib/gulp-usemin/lib/pipeline.js: -------------------------------------------------------------------------------- 1 | module.exports = function(name, files, tasks, push) { 2 | var through = require('through2'); 3 | var concat = require('gulp-concat')(name || 'filename.temp', {newLine: '\n'}); 4 | 5 | /* PREPARE TASKS */ 6 | tasks = (tasks || []).slice(); 7 | 8 | var concatIndex = tasks.indexOf('concat'); 9 | if (concatIndex == -1) 10 | tasks.unshift(concat); 11 | else 12 | tasks[concatIndex] = concat; 13 | 14 | tasks.push(through.obj(function(file, enc, streamCallback) { 15 | streamCallback(null, file); 16 | push(file); 17 | })); 18 | 19 | /* PREPARE TASKS END */ 20 | 21 | var stream = through.obj(function(file, enc, streamCallback) { 22 | streamCallback(null, file); 23 | }); 24 | var newStream = stream; 25 | tasks.forEach(function(task) { 26 | newStream = newStream.pipe(typeof(task) == 'function' ? task(): task); 27 | }); 28 | 29 | files.forEach(stream.write.bind(stream)); 30 | stream.end(); 31 | }; 32 | -------------------------------------------------------------------------------- /lib/gulp-usemin/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gulp-usemin", 3 | "version": "0.3.23", 4 | "description": "Replaces references to non-optimized scripts or stylesheets into a set of HTML files (or any templates/views).", 5 | "main": "index.js", 6 | "dependencies": { 7 | "gulp-util": "~2.2.14", 8 | "through2": "~0.5.1", 9 | "glob": "~4.0.4", 10 | "gulp-concat": "~2.4.1" 11 | }, 12 | "devDependencies": { 13 | "event-stream": "~3.1.0", 14 | "vinyl-fs": "~2.3.1", 15 | "gulp": "~3.8.6", 16 | "gulp-jshint": "~1.7.0", 17 | "gulp-mocha": "~0.5.1", 18 | "gulp-uglify": "~0.3.1", 19 | "gulp-minify-html": "~0.1.1", 20 | "gulp-minify-css": "~0.3.0", 21 | "gulp-rev": "~0.4.2", 22 | "gulp-less": "2.0.1" 23 | }, 24 | "scripts": { 25 | "test": "gulp" 26 | }, 27 | "repository": { 28 | "type": "git", 29 | "url": "git://github.com/zont/gulp-usemin.git" 30 | }, 31 | "keywords": [ 32 | "gulpplugin", 33 | "usemin", 34 | "gulp-usemin" 35 | ], 36 | "author": { 37 | "name": "Alexander Zonov", 38 | "email": "zont@pochta.ru" 39 | }, 40 | "license": "MIT", 41 | "bugs": { 42 | "url": "https://github.com/zont/gulp-usemin/issues" 43 | }, 44 | "engines": { 45 | "node": ">=0.12" 46 | }, 47 | "gitHead": "81bff9fd6d46f9fbedea53e349001d0fa7bcb49c", 48 | "homepage": "https://github.com/zont/gulp-usemin#readme", 49 | "_id": "gulp-usemin@0.3.23", 50 | "_shasum": "9f451546e4ac53a00119c7343d36c42d6a5ce6f0", 51 | "_from": "gulp-usemin@>=0.3.21 <0.4.0", 52 | "_resolved": "https://registry.npm.taobao.org/gulp-usemin/download/gulp-usemin-0.3.23.tgz", 53 | "_npmVersion": "3.3.10", 54 | "_nodeVersion": "5.0.0", 55 | "_npmUser": { 56 | "name": "alexander.zonov", 57 | "email": "zont@pochta.ru" 58 | }, 59 | "dist": { 60 | "shasum": "9f451546e4ac53a00119c7343d36c42d6a5ce6f0", 61 | "size": 12939, 62 | "noattachment": false, 63 | "tarball": "http://registry.npm.taobao.org/gulp-usemin/download/gulp-usemin-0.3.23.tgz" 64 | }, 65 | "maintainers": [ 66 | { 67 | "name": "alexander.zonov", 68 | "email": "zont@pochta.ru" 69 | } 70 | ], 71 | "_npmOperationalInternal": { 72 | "host": "packages-16-east.internal.npmjs.com", 73 | "tmp": "tmp/gulp-usemin-0.3.23.tgz_1462364006265_0.13037881650961936" 74 | }, 75 | "directories": {}, 76 | "publish_time": 1462364007542, 77 | "_cnpm_publish_time": 1462364007542 78 | } 79 | -------------------------------------------------------------------------------- /lib/gulp-usemin/test/expected/app.js: -------------------------------------------------------------------------------- 1 | var sample = 111; 2 | console.log(sample); -------------------------------------------------------------------------------- /lib/gulp-usemin/test/expected/array-js-attributes.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /lib/gulp-usemin/test/expected/build-remove-no-trailing-whitespace.html: -------------------------------------------------------------------------------- 1 |
-------------------------------------------------------------------------------- /lib/gulp-usemin/test/expected/complex-path.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /lib/gulp-usemin/test/expected/complex.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /lib/gulp-usemin/test/expected/conditional-complex.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /lib/gulp-usemin/test/expected/conditional-css.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/gulp-usemin/test/expected/conditional-inline-css.html: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /lib/gulp-usemin/test/expected/conditional-inline-js.html: -------------------------------------------------------------------------------- 1 | 3 | -------------------------------------------------------------------------------- /lib/gulp-usemin/test/expected/conditional-js.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/gulp-usemin/test/expected/data/css/min-style.css: -------------------------------------------------------------------------------- 1 | *{margin:0;padding:0}body{margin:10px} -------------------------------------------------------------------------------- /lib/gulp-usemin/test/expected/data/css/style.css: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0; 3 | padding: 0; 4 | } 5 | body { 6 | margin: 10px; 7 | } -------------------------------------------------------------------------------- /lib/gulp-usemin/test/expected/data/js/app.js: -------------------------------------------------------------------------------- 1 | var sample = 111; 2 | console.log(sample); -------------------------------------------------------------------------------- /lib/gulp-usemin/test/expected/data/js/app_min_concat.js: -------------------------------------------------------------------------------- 1 | var sample=111; 2 | console.log(sample); -------------------------------------------------------------------------------- /lib/gulp-usemin/test/expected/data/js/min-app.js: -------------------------------------------------------------------------------- 1 | var sample=111;console.log(sample); -------------------------------------------------------------------------------- /lib/gulp-usemin/test/expected/glob-inline-css.html: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /lib/gulp-usemin/test/expected/glob-inline-js.html: -------------------------------------------------------------------------------- 1 | 3 | -------------------------------------------------------------------------------- /lib/gulp-usemin/test/expected/many-blocks-removal.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/gulp-usemin/test/expected/min-app.js: -------------------------------------------------------------------------------- 1 | var sample=111;console.log(sample); -------------------------------------------------------------------------------- /lib/gulp-usemin/test/expected/min-complex-path.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/gulp-usemin/test/expected/min-complex.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/gulp-usemin/test/expected/min-css-with-media-query.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/gulp-usemin/test/expected/min-html-simple-css.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/gulp-usemin/test/expected/min-html-simple-js.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/gulp-usemin/test/expected/min-html-simple-removal.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xunuoi/gulpman/d12eb0fa1c71a955f98d2c4bb7090540a518dd7a/lib/gulp-usemin/test/expected/min-html-simple-removal.html -------------------------------------------------------------------------------- /lib/gulp-usemin/test/expected/min-paths-with-querystring.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/gulp-usemin/test/expected/min-simple-css-path.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/gulp-usemin/test/expected/min-simple-css.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/gulp-usemin/test/expected/min-simple-js-path.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/gulp-usemin/test/expected/min-simple-js.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/gulp-usemin/test/expected/min-style.css: -------------------------------------------------------------------------------- 1 | *{margin:0;padding:0}body{margin:10px} -------------------------------------------------------------------------------- /lib/gulp-usemin/test/expected/multiple-alternative-paths.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /lib/gulp-usemin/test/expected/multiple-files.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /lib/gulp-usemin/test/expected/paths-with-querystring.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /lib/gulp-usemin/test/expected/simple-css-path.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/gulp-usemin/test/expected/simple-css.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/gulp-usemin/test/expected/simple-inline-css.html: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /lib/gulp-usemin/test/expected/simple-inline-js.html: -------------------------------------------------------------------------------- 1 | 3 | -------------------------------------------------------------------------------- /lib/gulp-usemin/test/expected/simple-js-path.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/gulp-usemin/test/expected/simple-js-removal.html: -------------------------------------------------------------------------------- 1 |
-------------------------------------------------------------------------------- /lib/gulp-usemin/test/expected/simple-js.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/gulp-usemin/test/expected/single-quotes-css.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/gulp-usemin/test/expected/single-quotes-inline-css.html: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /lib/gulp-usemin/test/expected/single-quotes-inline-js.html: -------------------------------------------------------------------------------- 1 | 3 | -------------------------------------------------------------------------------- /lib/gulp-usemin/test/expected/single-quotes-js.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/gulp-usemin/test/expected/style.css: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0; 3 | padding: 0; 4 | } 5 | body { 6 | margin: 10px; 7 | } -------------------------------------------------------------------------------- /lib/gulp-usemin/test/expected/subfolder/app.js: -------------------------------------------------------------------------------- 1 | console.log(sample); -------------------------------------------------------------------------------- /lib/gulp-usemin/test/expected/subfolder/index.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/gulp-usemin/test/fixtures/alternative/js/util.js: -------------------------------------------------------------------------------- 1 | alert(1); 2 | -------------------------------------------------------------------------------- /lib/gulp-usemin/test/fixtures/array-js-attributes.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /lib/gulp-usemin/test/fixtures/async-less.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /lib/gulp-usemin/test/fixtures/build-remove-no-trailing-whitespace.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |

lorem ipsum

4 |

lorem ipsum

5 | 6 | 7 |

lorem ipsum

8 |

lorem ipsum

9 | 10 | 11 |

lorem ipsum

12 |

lorem ipsum

13 | 14 | 15 |

lorem ipsum

16 |

lorem ipsum

17 | 18 |
19 | -------------------------------------------------------------------------------- /lib/gulp-usemin/test/fixtures/comment-js.html: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /lib/gulp-usemin/test/fixtures/complex-path.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /lib/gulp-usemin/test/fixtures/complex.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /lib/gulp-usemin/test/fixtures/conditional-complex.html: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 13 | -------------------------------------------------------------------------------- /lib/gulp-usemin/test/fixtures/conditional-css.html: -------------------------------------------------------------------------------- 1 | 2 | 6 | -------------------------------------------------------------------------------- /lib/gulp-usemin/test/fixtures/conditional-inline-css.html: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | -------------------------------------------------------------------------------- /lib/gulp-usemin/test/fixtures/conditional-inline-js.html: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | -------------------------------------------------------------------------------- /lib/gulp-usemin/test/fixtures/conditional-js.html: -------------------------------------------------------------------------------- 1 | 2 | 6 | -------------------------------------------------------------------------------- /lib/gulp-usemin/test/fixtures/css-with-media-query-error.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /lib/gulp-usemin/test/fixtures/css-with-media-query.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /lib/gulp-usemin/test/fixtures/css/clear.css: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0; 3 | padding: 0; 4 | } -------------------------------------------------------------------------------- /lib/gulp-usemin/test/fixtures/css/main.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 10px; 3 | } -------------------------------------------------------------------------------- /lib/gulp-usemin/test/fixtures/glob-css.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /lib/gulp-usemin/test/fixtures/glob-inline-css.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /lib/gulp-usemin/test/fixtures/glob-inline-js.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /lib/gulp-usemin/test/fixtures/glob-js.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /lib/gulp-usemin/test/fixtures/js/lib.js: -------------------------------------------------------------------------------- 1 | var sample = 111; -------------------------------------------------------------------------------- /lib/gulp-usemin/test/fixtures/js/main.js: -------------------------------------------------------------------------------- 1 | console.log(sample); -------------------------------------------------------------------------------- /lib/gulp-usemin/test/fixtures/js2/lib2.js: -------------------------------------------------------------------------------- 1 | var sample = 111; -------------------------------------------------------------------------------- /lib/gulp-usemin/test/fixtures/js2/main2.js: -------------------------------------------------------------------------------- 1 | console.log(sample); -------------------------------------------------------------------------------- /lib/gulp-usemin/test/fixtures/less/clear.less: -------------------------------------------------------------------------------- 1 | @zero: 0; 2 | 3 | * { 4 | margin: @zero; 5 | padding: @zero; 6 | } -------------------------------------------------------------------------------- /lib/gulp-usemin/test/fixtures/less/main.less: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 10px; 3 | } -------------------------------------------------------------------------------- /lib/gulp-usemin/test/fixtures/many-blocks-removal.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /lib/gulp-usemin/test/fixtures/many-blocks.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /lib/gulp-usemin/test/fixtures/min-html-simple-css.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/gulp-usemin/test/fixtures/min-html-simple-js.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/gulp-usemin/test/fixtures/min-html-simple-removal.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /lib/gulp-usemin/test/fixtures/multiple-alternative-paths-inline.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /lib/gulp-usemin/test/fixtures/multiple-alternative-paths.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /lib/gulp-usemin/test/fixtures/multiple-files.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /lib/gulp-usemin/test/fixtures/paths-with-querystring.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /lib/gulp-usemin/test/fixtures/simple-css-alternate-path.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /lib/gulp-usemin/test/fixtures/simple-css-path.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /lib/gulp-usemin/test/fixtures/simple-css.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /lib/gulp-usemin/test/fixtures/simple-inline-css.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /lib/gulp-usemin/test/fixtures/simple-inline-js.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /lib/gulp-usemin/test/fixtures/simple-js-alternate-path.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /lib/gulp-usemin/test/fixtures/simple-js-path.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /lib/gulp-usemin/test/fixtures/simple-js-removal.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 5 | 6 |
7 | -------------------------------------------------------------------------------- /lib/gulp-usemin/test/fixtures/simple-js.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /lib/gulp-usemin/test/fixtures/single-quotes-css.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /lib/gulp-usemin/test/fixtures/single-quotes-inline-css.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /lib/gulp-usemin/test/fixtures/single-quotes-inline-js.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /lib/gulp-usemin/test/fixtures/single-quotes-js.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /lib/gulp-usemin/test/fixtures/subfolder/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /lib/gulp-usemin/test/fixtures/subfolder/script.js: -------------------------------------------------------------------------------- 1 | console.log(sample); -------------------------------------------------------------------------------- /lib/gulp-usemin/test/main.js: -------------------------------------------------------------------------------- 1 | /* jshint node: true */ 2 | /* global describe, it */ 3 | 4 | 'use strict'; 5 | 6 | var assert = require('assert'); 7 | var fs = require('fs'); 8 | var gutil = require('gulp-util'); 9 | var PassThrough = require('stream').PassThrough; 10 | var path = require('path'); 11 | var usemin = require('../index'); 12 | var vfs = require('vinyl-fs'); 13 | 14 | function getFile(filePath) { 15 | return new gutil.File({ 16 | path: filePath, 17 | base: path.dirname(filePath), 18 | contents: fs.readFileSync(filePath) 19 | }); 20 | } 21 | 22 | function getFixture(filePath) { 23 | return getFile(path.join('test', 'fixtures', filePath)); 24 | } 25 | 26 | function getExpected(filePath) { 27 | return getFile(path.join('test', 'expected', filePath)); 28 | } 29 | 30 | describe('gulp-usemin', function() { 31 | describe('allow removal sections', function() { 32 | function compare(name, expectedName, done) { 33 | var htmlmin = require('gulp-minify-html'); 34 | var stream = usemin({html: [htmlmin({empty: true, quotes: true})]}); 35 | 36 | stream.on('data', function(newFile) { 37 | if (path.basename(newFile.path) === name) { 38 | assert.equal(String(getExpected(expectedName).contents), String(newFile.contents)); 39 | done(); 40 | } 41 | }); 42 | 43 | stream.write(getFixture(name)); 44 | } 45 | 46 | it('simple js block', function(done) { 47 | compare('simple-js-removal.html', 'simple-js-removal.html', done); 48 | }); 49 | 50 | it('minified js block', function(done) { 51 | compare('min-html-simple-removal.html', 'min-html-simple-removal.html', done); 52 | }); 53 | 54 | it('many blocks', function(done) { 55 | compare('many-blocks-removal.html', 'many-blocks-removal.html', done); 56 | }); 57 | 58 | it('robust pattern recognition (no whitespace after build:remove)', function(done) { 59 | compare('build-remove-no-trailing-whitespace.html', 'build-remove-no-trailing-whitespace.html', done); 60 | }); 61 | 62 | }); 63 | 64 | describe('negative test:', function() { 65 | it('shouldn\'t work in stream mode', function(done) { 66 | var stream = usemin(); 67 | var t; 68 | var fakeStream = new PassThrough(); 69 | var fakeFile = new gutil.File({ 70 | contents: fakeStream 71 | }); 72 | fakeStream.end(); 73 | 74 | stream.on('error', function() { 75 | clearTimeout(t); 76 | done(); 77 | }); 78 | 79 | t = setTimeout(function() { 80 | assert.fail('', '', 'Should throw error', ''); 81 | done(); 82 | }, 1000); 83 | 84 | stream.write(fakeFile); 85 | }); 86 | 87 | it('html without blocks', function(done) { 88 | var stream = usemin(); 89 | var content = '
content
'; 90 | var fakeFile = new gutil.File({ 91 | path: 'test.file', 92 | contents: new Buffer(content) 93 | }); 94 | 95 | stream.on('data', function(newFile) { 96 | assert.equal(content, String(newFile.contents)); 97 | done(); 98 | }); 99 | 100 | stream.write(fakeFile); 101 | }); 102 | }); 103 | 104 | describe('should work in buffer mode with', function() { 105 | describe('minified HTML:', function() { 106 | function compare(name, expectedName, done, fail) { 107 | var htmlmin = require('gulp-minify-html'); 108 | var stream = usemin({ 109 | html: [function() { 110 | return htmlmin({empty: true}); 111 | }] 112 | }); 113 | 114 | stream.on('data', function(newFile) { 115 | if (path.basename(newFile.path) === name) { 116 | assert.equal(String(getExpected(expectedName).contents), String(newFile.contents)); 117 | done(); 118 | } 119 | }); 120 | stream.on('error', function() { 121 | if (fail) 122 | fail(); 123 | }); 124 | 125 | stream.write(getFixture(name)); 126 | } 127 | 128 | it('simple js block', function(done) { 129 | compare('simple-js.html', 'min-simple-js.html', done); 130 | }); 131 | 132 | it('simple js block with path', function(done) { 133 | compare('simple-js-path.html', 'min-simple-js-path.html', done); 134 | }); 135 | 136 | it('simple css block', function(done) { 137 | compare('simple-css.html', 'min-simple-css.html', done); 138 | }); 139 | 140 | it('css block with media query', function(done) { 141 | compare('css-with-media-query.html', 'min-css-with-media-query.html', done); 142 | }); 143 | 144 | it('css block with mixed incompatible media queries should error', function(done) { 145 | compare('css-with-media-query-error.html', 'min-css-with-media-query.html', function() { 146 | assert.fail('', '', 'should error', ''); 147 | done(); 148 | }, done); 149 | }); 150 | 151 | it('simple css block with path', function(done) { 152 | compare('simple-css-path.html', 'min-simple-css-path.html', done); 153 | }); 154 | 155 | it('complex (css + js)', function(done) { 156 | compare('complex.html', 'min-complex.html', done); 157 | }); 158 | 159 | it('complex with path (css + js)', function(done) { 160 | compare('complex-path.html', 'min-complex-path.html', done); 161 | }); 162 | 163 | it('paths with querystring', function(done) { 164 | compare('paths-with-querystring.html', 'min-paths-with-querystring.html', done); 165 | }); 166 | }); 167 | 168 | describe('not minified HTML:', function() { 169 | function compare(name, expectedName, done) { 170 | var stream = usemin(); 171 | 172 | stream.on('data', function(newFile) { 173 | if (path.basename(newFile.path) === name) { 174 | assert.equal(String(getExpected(expectedName).contents), String(newFile.contents)); 175 | done(); 176 | } 177 | }); 178 | 179 | stream.write(getFixture(name)); 180 | } 181 | 182 | it('simple js block with single quotes', function (done) { 183 | compare('single-quotes-js.html', 'single-quotes-js.html', done); 184 | }); 185 | 186 | it('simple css block with single quotes', function (done) { 187 | compare('single-quotes-css.html', 'single-quotes-css.html', done); 188 | }); 189 | 190 | it('simple (js block)', function(done) { 191 | compare('simple-js.html', 'simple-js.html', done); 192 | }); 193 | 194 | it('simple (js block) (html minified)', function(done) { 195 | compare('min-html-simple-js.html', 'min-html-simple-js.html', done); 196 | }); 197 | 198 | it('simple with path (js block)', function(done) { 199 | compare('simple-js-path.html', 'simple-js-path.html', done); 200 | }); 201 | 202 | it('simple (css block)', function(done) { 203 | compare('simple-css.html', 'simple-css.html', done); 204 | }); 205 | 206 | it('simple (css block) (html minified)', function(done) { 207 | compare('min-html-simple-css.html', 'min-html-simple-css.html', done); 208 | }); 209 | 210 | it('simple with path (css block)', function(done) { 211 | compare('simple-css-path.html', 'simple-css-path.html', done); 212 | }); 213 | 214 | it('complex (css + js)', function(done) { 215 | compare('complex.html', 'complex.html', done); 216 | }); 217 | 218 | it('complex with path (css + js)', function(done) { 219 | compare('complex-path.html', 'complex-path.html', done); 220 | }); 221 | 222 | it('multiple alternative paths', function(done) { 223 | compare('multiple-alternative-paths.html', 'multiple-alternative-paths.html', done); 224 | }); 225 | 226 | it('paths with querystring', function(done) { 227 | compare('paths-with-querystring.html', 'paths-with-querystring.html', done); 228 | }); 229 | }); 230 | 231 | describe('minified CSS:', function() { 232 | function compare(fixtureName, name, expectedName, end) { 233 | var cssmin = require('gulp-minify-css'); 234 | var stream = usemin({css: ['concat', cssmin()]}); 235 | 236 | stream.on('data', function(newFile) { 237 | if (path.basename(newFile.path) === name) { 238 | assert.equal(String(getExpected(expectedName).contents), String(newFile.contents)); 239 | end(); 240 | } 241 | }); 242 | 243 | stream.write(getFixture(fixtureName)); 244 | } 245 | 246 | it('simple (css block)', function(done) { 247 | var name = 'style.css'; 248 | var expectedName = 'min-style.css'; 249 | 250 | compare('simple-css.html', name, expectedName, done); 251 | }); 252 | 253 | it('simple with path (css block)', function(done) { 254 | var name = 'style.css'; 255 | var expectedName = path.join('data', 'css', 'min-style.css'); 256 | 257 | compare('simple-css-path.html', name, expectedName, done); 258 | }); 259 | 260 | it('simple with alternate path (css block)', function(done) { 261 | var name = 'style.css'; 262 | var expectedName = path.join('data', 'css', 'min-style.css'); 263 | 264 | compare('simple-css-alternate-path.html', name, expectedName, done); 265 | }); 266 | }); 267 | 268 | describe('not minified CSS:', function() { 269 | function compare(fixtureName, expectedName, end) { 270 | var stream = usemin(); 271 | 272 | stream.on('data', function(newFile) { 273 | if (path.basename(newFile.path) === path.basename(expectedName)) { 274 | assert.equal(String(getExpected(expectedName).contents), String(newFile.contents)); 275 | end(); 276 | } 277 | }); 278 | 279 | stream.write(getFixture(fixtureName)); 280 | } 281 | 282 | it('simple (css block)', function(done) { 283 | compare('simple-css.html', 'style.css', done); 284 | }); 285 | 286 | it('simple (css block) (minified html)', function(done) { 287 | compare('min-html-simple-css.html', 'style.css', done); 288 | }); 289 | 290 | it('simple with path (css block)', function(done) { 291 | compare('simple-css-path.html', path.join('data', 'css', 'style.css'), done); 292 | }); 293 | 294 | it('simple with alternate path (css block)', function(done) { 295 | compare('simple-css-alternate-path.html', path.join('data', 'css', 'style.css'), done); 296 | }); 297 | }); 298 | 299 | describe('minified JS:', function() { 300 | function compare(fixtureName, name, expectedName, end) { 301 | var jsmin = require('gulp-uglify'); 302 | var stream = usemin({js: [jsmin()]}); 303 | 304 | stream.on('data', function(newFile) { 305 | if (path.basename(newFile.path) === path.basename(name)) { 306 | assert.equal(String(getExpected(expectedName).contents), String(newFile.contents)); 307 | end(); 308 | } 309 | }); 310 | 311 | stream.write(getFixture(fixtureName)); 312 | } 313 | 314 | it('simple (js block)', function(done) { 315 | compare('simple-js.html', 'app.js', 'min-app.js', done); 316 | }); 317 | 318 | it('simple with path (js block)', function(done) { 319 | var name = path.join('data', 'js', 'app.js'); 320 | var expectedName = path.join('data', 'js', 'min-app.js'); 321 | 322 | compare('simple-js-path.html', name, expectedName, done); 323 | }); 324 | 325 | it('simple with alternate path (js block)', function(done) { 326 | var name = path.join('data', 'js', 'app.js'); 327 | var expectedName = path.join('data', 'js', 'min-app.js'); 328 | 329 | compare('simple-js-alternate-path.html', name, expectedName, done); 330 | }); 331 | }); 332 | 333 | describe('not minified JS:', function() { 334 | function compare(fixtureName, expectedName, end) { 335 | var stream = usemin(); 336 | 337 | stream.on('data', function(newFile) { 338 | if (path.basename(newFile.path) === path.basename(expectedName)) { 339 | assert.equal(String(getExpected(expectedName).contents), String(newFile.contents)); 340 | end(); 341 | } 342 | }); 343 | 344 | stream.write(getFixture(fixtureName)); 345 | } 346 | 347 | it('simple (js block)', function(done) { 348 | compare('simple-js.html', 'app.js', done); 349 | }); 350 | 351 | it('simple (js block) (minified html)', function(done) { 352 | compare('min-html-simple-js.html', 'app.js', done); 353 | }); 354 | 355 | it('simple with path (js block)', function(done) { 356 | compare('simple-js-path.html', path.join('data', 'js', 'app.js'), done); 357 | }); 358 | 359 | it('simple with alternate path (js block)', function(done) { 360 | compare('simple-js-alternate-path.html', path.join('data', 'js', 'app.js'), done); 361 | }); 362 | }); 363 | 364 | it('many html files', function(done) { 365 | var cssmin = require('gulp-minify-css'); 366 | var jsmin = require('gulp-uglify'); 367 | var rev = require('gulp-rev'); 368 | var stream = usemin({ 369 | css: ['concat', cssmin], 370 | js: ['concat', jsmin] 371 | }); 372 | 373 | var nameCss = 'style.css'; 374 | var expectedNameCss = 'min-style.css'; 375 | var nameJs = 'app.js'; 376 | var expectedNameJs = 'min-app.js'; 377 | var cssExist = false; 378 | var jsExist = false; 379 | var htmlCount = 0; 380 | 381 | stream.on('data', function(newFile) { 382 | if (path.basename(newFile.path) === path.basename(nameCss)) { 383 | cssExist = true; 384 | assert.equal(String(getExpected(expectedNameCss).contents), String(newFile.contents)); 385 | } 386 | else if (path.basename(newFile.path) === path.basename(nameJs)) { 387 | jsExist = true; 388 | assert.equal(String(getExpected(expectedNameJs).contents), String(newFile.contents)); 389 | } 390 | else { 391 | htmlCount += 1; 392 | } 393 | }); 394 | 395 | stream.on('end', function() { 396 | assert.equal(htmlCount, 2); 397 | assert.ok(cssExist); 398 | assert.ok(jsExist); 399 | done(); 400 | }); 401 | 402 | stream.write(getFixture('simple-css.html')); 403 | stream.write(getFixture('simple-js.html')); 404 | stream.end(); 405 | }); 406 | 407 | it('many blocks', function(done) { 408 | var cssmin = require('gulp-minify-css'); 409 | var jsmin = require('gulp-uglify'); 410 | var rev = require('gulp-rev'); 411 | var stream = usemin({ 412 | css1: ['concat', cssmin], 413 | js1: [jsmin, 'concat', rev] 414 | }); 415 | 416 | var nameCss = path.join('data', 'css', 'style.css'); 417 | var expectedNameCss = path.join('data', 'css', 'min-style.css'); 418 | var nameJs = path.join('data', 'js', 'app.js'); 419 | var expectedNameJs = path.join('data', 'js', 'app.js'); 420 | var nameJs1 = 'app1'; 421 | var expectedNameJs1 = path.join('data', 'js', 'app_min_concat.js'); 422 | var nameJs2 = 'app2'; 423 | var expectedNameJs2 = path.join('data', 'js', 'app_min_concat.js'); 424 | var cssExist = false; 425 | var jsExist = false; 426 | var js1Exist = false; 427 | var js2Exist = false; 428 | 429 | stream.on('data', function(newFile) { 430 | if (path.basename(newFile.path) === path.basename(nameCss)) { 431 | cssExist = true; 432 | assert.equal(String(getExpected(expectedNameCss).contents), String(newFile.contents)); 433 | } 434 | else if (path.basename(newFile.path) === path.basename(nameJs)) { 435 | jsExist = true; 436 | assert.equal(String(getExpected(expectedNameJs).contents), String(newFile.contents)); 437 | } 438 | else if (newFile.path.indexOf(nameJs1) != -1) { 439 | js1Exist = true; 440 | assert.equal(String(getExpected(expectedNameJs1).contents), String(newFile.contents)); 441 | } 442 | else if (newFile.path.indexOf(nameJs2) != -1) { 443 | js2Exist = true; 444 | assert.equal(String(getExpected(expectedNameJs2).contents), String(newFile.contents)); 445 | } 446 | else { 447 | assert.ok(cssExist); 448 | assert.ok(jsExist); 449 | assert.ok(js1Exist); 450 | assert.ok(js2Exist); 451 | done(); 452 | } 453 | }); 454 | 455 | stream.write(getFixture('many-blocks.html')); 456 | }); 457 | 458 | describe('assetsDir option:', function() { 459 | function compare(assetsDir, done) { 460 | var stream = usemin({assetsDir: assetsDir}); 461 | var expectedName = 'style.css'; 462 | 463 | stream.on('data', function(newFile) { 464 | if (path.basename(newFile.path) === expectedName) { 465 | assert.equal(String(getExpected(expectedName).contents), String(newFile.contents)); 466 | done(); 467 | } 468 | }); 469 | 470 | stream.write(getFixture('simple-css.html')); 471 | } 472 | 473 | it('absolute path', function(done) { 474 | compare(path.join(process.cwd(), 'test', 'fixtures'), done); 475 | }); 476 | 477 | it('relative path', function(done) { 478 | compare(path.join('test', 'fixtures'), done); 479 | }); 480 | }); 481 | 482 | describe('conditional comments:', function() { 483 | function compare(name, expectedName, done) { 484 | var stream = usemin(); 485 | 486 | stream.on('data', function(newFile) { 487 | if (path.basename(newFile.path) === name) { 488 | assert.equal(String(getExpected(expectedName).contents), String(newFile.contents)); 489 | done(); 490 | } 491 | }); 492 | 493 | stream.write(getFixture(name)); 494 | } 495 | 496 | it('conditional (js block)', function(done) { 497 | compare('conditional-js.html', 'conditional-js.html', done); 498 | }); 499 | 500 | it('conditional (css block)', function(done) { 501 | compare('conditional-css.html', 'conditional-css.html', done); 502 | }); 503 | 504 | it('conditional (css + js)', function(done) { 505 | compare('conditional-complex.html', 'conditional-complex.html', done); 506 | }); 507 | 508 | it('conditional (inline js block)', function(done) { 509 | compare('conditional-inline-js.html', 'conditional-inline-js.html', done); 510 | }); 511 | 512 | it('conditional (inline css block)', function(done) { 513 | compare('conditional-inline-css.html', 'conditional-inline-css.html', done); 514 | }); 515 | 516 | }); 517 | 518 | describe('globbed files:', function() { 519 | function compare(fixtureName, name, end) { 520 | var stream = usemin(); 521 | 522 | stream.on('data', function(newFile) { 523 | if (path.basename(newFile.path) === name) { 524 | assert.equal(String(newFile.contents), String(getExpected(name).contents)); 525 | end(); 526 | } 527 | }); 528 | 529 | stream.write(getFixture(fixtureName)); 530 | } 531 | 532 | it('glob (js block)', function(done) { 533 | compare('glob-js.html', 'app.js', done); 534 | }); 535 | 536 | it('glob (css block)', function(done) { 537 | compare('glob-css.html', 'style.css', done); 538 | }); 539 | 540 | it('glob inline (js block)', function(done) { 541 | compare('glob-inline-js.html', 'glob-inline-js.html', done); 542 | }); 543 | 544 | it('glob inline (css block)', function(done) { 545 | compare('glob-inline-css.html', 'glob-inline-css.html', done); 546 | }); 547 | }); 548 | 549 | describe('comment files:', function() { 550 | function compare(name, callback) { 551 | var stream = usemin({enableHtmlComment: true}); 552 | 553 | stream.on('data', callback); 554 | 555 | stream.write(getFixture(name)); 556 | } 557 | 558 | it('comment (js block)', function(done) { 559 | var expectedName = 'app.js'; 560 | 561 | compare( 562 | 'comment-js.html', 563 | function(newFile) { 564 | if (path.basename(newFile.path) === expectedName) { 565 | assert.equal(String(newFile.contents), String(getExpected(expectedName).contents)); 566 | done(); 567 | } 568 | } 569 | ); 570 | }); 571 | }); 572 | 573 | describe('inline Sources:', function() { 574 | function compare(fixtureName, name, end) { 575 | var stream = usemin(); 576 | 577 | stream.on('data', function(newFile) { 578 | if (path.basename(newFile.path) === name) { 579 | assert.equal(String(newFile.contents), String(getExpected(name).contents)); 580 | end(); 581 | } 582 | }); 583 | 584 | stream.write(getFixture(fixtureName)); 585 | } 586 | 587 | it('simple inline js block', function (done) { 588 | compare('simple-inline-js.html', 'simple-inline-js.html', done); 589 | }); 590 | 591 | it('simple inline css block', function (done) { 592 | compare('simple-inline-css.html', 'simple-inline-css.html', done); 593 | }); 594 | 595 | it('simple inline js block width single quotes', function (done) { 596 | compare('single-quotes-inline-js.html', 'single-quotes-inline-js.html', done); 597 | }); 598 | 599 | it('simple inline css block with single quotes', function (done) { 600 | compare('single-quotes-inline-css.html', 'single-quotes-inline-css.html', done); 601 | }); 602 | 603 | }); 604 | 605 | describe('array jsAttributes:', function() { 606 | 607 | function compare(fixtureName, name, end) { 608 | var stream = usemin({ 609 | jsAttributes: { 610 | seq: [1, 2, 1, 3], 611 | color: ['blue', 'red', 'yellow', 'pink'] 612 | }, 613 | js: [], 614 | js1: [], 615 | js2: [], 616 | js3: [] 617 | 618 | }); 619 | 620 | stream.on('data', function(newFile) { 621 | if (path.basename(newFile.path) === name) { 622 | assert.equal(String(newFile.contents), String(getExpected(name).contents)); 623 | end(); 624 | } 625 | }); 626 | 627 | stream.write(getFixture(fixtureName)); 628 | } 629 | 630 | it('js attributes with array define', function (done) { 631 | compare('array-js-attributes.html', 'array-js-attributes.html', done); 632 | }); 633 | 634 | }); 635 | 636 | it('async task', function(done) { 637 | var less = require('gulp-less'); 638 | var cssmin = require('gulp-minify-css'); 639 | var stream = usemin({ 640 | less: [less(), 'concat', cssmin()] 641 | }); 642 | 643 | var name = 'style.css'; 644 | var expectedName = 'min-style.css'; 645 | 646 | stream.on('data', function(newFile) { 647 | if (path.basename(newFile.path) === path.basename(name)) { 648 | assert.equal(String(getExpected(expectedName).contents), String(newFile.contents)); 649 | done(); 650 | } 651 | }); 652 | 653 | stream.write(getFixture('async-less.html')); 654 | }); 655 | 656 | it('subfolders', function(done) { 657 | var stream = usemin(); 658 | var jsExist = false; 659 | var nameJs = path.join('subfolder', 'app.js'); 660 | 661 | stream.on('data', function(newFile) { 662 | if (path.basename(newFile.path) === path.basename(nameJs)) { 663 | jsExist = true; 664 | assert.equal(path.relative(newFile.base, newFile.path), nameJs); 665 | assert.equal(String(getExpected(nameJs).contents), String(newFile.contents)); 666 | } 667 | else { 668 | assert.ok(jsExist); 669 | done(); 670 | } 671 | }); 672 | 673 | vfs.src('test/fixtures/**/index.html') 674 | .pipe(stream); 675 | }); 676 | }); 677 | 678 | it('multiple files in stream', function(done) { 679 | var multipleFiles = function() { 680 | var through = require('through2'); 681 | var File = gutil.File; 682 | 683 | return through.obj(function(file) { 684 | var stream = this; 685 | 686 | stream.push(new File({ 687 | cwd: file.cwd, 688 | base: file.base, 689 | path: file.path, 690 | contents: new Buffer('test1') 691 | })); 692 | 693 | stream.push(new File({ 694 | cwd: file.cwd, 695 | base: file.base, 696 | path: file.path, 697 | contents: new Buffer('test2') 698 | })); 699 | }); 700 | }; 701 | var stream = usemin({ 702 | css: [multipleFiles], 703 | js: [multipleFiles] 704 | }); 705 | 706 | stream.on('data', function(newFile) { 707 | if (path.basename(newFile.path) === path.basename('multiple-files.html')) { 708 | assert.equal(String(getExpected('multiple-files.html').contents), String(newFile.contents)); 709 | done(); 710 | } 711 | }); 712 | 713 | stream.write(getFixture('multiple-files.html')); 714 | }); 715 | }); 716 | -------------------------------------------------------------------------------- /lib/htmlPathParser.js: -------------------------------------------------------------------------------- 1 | /** 2 | * FOR PARSE HTML REF PATH 3 | */ 4 | 5 | 'use strict' 6 | 7 | 8 | var through = require('through2'), 9 | path = require('path'), 10 | j = path.join, 11 | // gulpman utils 12 | gmutil = require('./gmutil') 13 | 14 | 15 | 16 | function _dealHTML(conf){ 17 | 18 | let _opts = conf['_opts'], 19 | basepath = conf['basepath'], 20 | _isRuntimeDir = conf['_isRuntimeDir'], 21 | all_raw_source_reg = conf['all_raw_source_reg'] 22 | 23 | 24 | return through.obj(function (file, enc, cb){ 25 | 26 | if (file.isNull()) { 27 | this.push(file); 28 | return cb() 29 | } 30 | if (file.isStream()) { 31 | gmutil.error('*ParseHtml Error: Streaming not supported') 32 | return cb() 33 | } 34 | 35 | let contents = file.contents.toString() 36 | 37 | // file.relative 是根据path.base自动生成的 38 | if(basepath) file.base = basepath 39 | 40 | let fdirname = path.dirname(file.relative) 41 | // set assets url prefix 42 | let _urlPrefix 43 | 44 | 45 | if(_opts['is_absolute']) { 46 | _urlPrefix = _opts['url_prefix'] 47 | }else { 48 | // 判断打包资源中的url路径前缀 49 | let _fPath = j(_isRuntimeDir ? _opts['runtime_views'] : _opts['dist_views'], fdirname) 50 | 51 | let _staticPath = _isRuntimeDir ? _opts['runtime_static'] : _opts['dist_static'] 52 | 53 | _urlPrefix = path.relative(_fPath, _staticPath) 54 | } 55 | 56 | 57 | // 所有格式都要处理 58 | let srcQuoteReg = new RegExp('(?=[\'"]?)([\\w\\.\\-\\?\\-\\/\\:]+?(\\.('+all_raw_source_reg+')))(?=\\?_gm_inline)?(?=[\'"]?)', 'gm') 59 | // httpReg = /^http(s)?\:/ 60 | 61 | 62 | let tmp_rs_list = [], 63 | rs_list = [] 64 | 65 | // 提取单标签和双标签 66 | tmp_rs_list = tmp_rs_list 67 | .concat(contents.match(gmutil.reg['tagMedia'])) 68 | .concat(contents.match(gmutil.reg['closeTagMedia'])) 69 | 70 | // 首先提取标签,然后从标签中提取href或者src 71 | tmp_rs_list.length && ( 72 | rs_list = tmp_rs_list 73 | .filter(r=>(r && r.match(srcQuoteReg))) 74 | .map(v=>v.match(srcQuoteReg)[0]) 75 | .filter(r=>{ 76 | // remove the http:xxx.com/xx and base64 data url 77 | // 只处理相对路径 78 | // 不处理绝对路径、http、dataURL 79 | return gmutil.isUrl('relative', r) 80 | }) 81 | .filter(r=>!r.match(/^(['"]\/)/gm)) 82 | ) 83 | 84 | // 这里利用set做去重 85 | let rs_set = new Set(rs_list), 86 | srcPrefix = j(_urlPrefix, fdirname) 87 | 88 | // 替换url的的path和前缀 89 | rs_set.size && rs_set.forEach(epath=>{ 90 | // 对于base64的参数标识要保留,不能清理掉,因为后续要嵌入base64 91 | let innerReg = new RegExp('(?=[\'"]?)('+epath+')(\\?_gm_inline)*(?=[\'"]?)', 'gm') 92 | 93 | contents = contents.replace(innerReg, j(srcPrefix, epath)+'$2') 94 | }) 95 | 96 | // dealing with usemin build mark syntax 97 | // when in publish, not runtime-dir 98 | if(!_isRuntimeDir) { 99 | // 这里处理usemin 的build的注释内容 100 | contents = gmutil.replaceBuildBlock(contents, srcPrefix) 101 | } 102 | 103 | file.contents = new Buffer(contents) 104 | 105 | gmutil.tip('*Raw HTML File Parsed: '+file.relative) 106 | 107 | this.push(file) 108 | cb() 109 | }) 110 | } 111 | 112 | 113 | module.exports = _dealHTML 114 | -------------------------------------------------------------------------------- /lib/iconFonter.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015 App Annie Inc. All rights reserved. 2 | 'use strict'; 3 | 4 | var path = require('path'), 5 | j = path.join; 6 | 7 | var sh = require("shelljs") 8 | 9 | var fontName = 'gmicon'; 10 | 11 | function registerTask(gulp, opts) { 12 | var runTimestamp = Math.round(Date.now() / 1000); 13 | 14 | if(!opts['iconfont']) { 15 | opts['iconfont'] = { 16 | 17 | } 18 | } 19 | var _iconConf = opts['iconfont']; 20 | 21 | var svgSource = _iconConf['source'] || j(opts['components'], 'iconfonts/source') 22 | var iconPath = _iconConf['icon'] || j(opts['components'], 'iconfonts', fontName) 23 | // var iconCSSPath = _iconConf['css'] || j(opts['components'], 'iconfonts') 24 | var svgSource = j(svgSource, '/*.svg'); 25 | 26 | gulp.task('gm:iconfont', function() { 27 | 28 | var iconfont = require('gulp-iconfont'); 29 | var iconfontCss = require('gulp-iconfont-css'); 30 | 31 | return gulp.src(svgSource) 32 | .pipe(iconfontCss({ 33 | fontName: fontName, 34 | // path: 'app/assets/css/templates/_icons.scss', 35 | targetPath: 'gmicon.css', 36 | fontPath: './', 37 | cssClass: 'gmicon' 38 | })) 39 | .pipe(iconfont({ 40 | fontName: fontName, // required 41 | formats: ['svg', 'ttf', 'eot', 'woff', 'woff2'], 42 | normalize: true, 43 | fontHeight: 500, 44 | timestamp: runTimestamp // recommended to get consistent builds when watching files 45 | })) 46 | .pipe(gulp.dest(iconPath)) 47 | }); 48 | } 49 | 50 | exports.registerTask = registerTask; 51 | -------------------------------------------------------------------------------- /lib/inline.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Rodey on 2015/11/5. 3 | */ 4 | 5 | var fs = require('fs'), 6 | path = require('path'), 7 | through2 = require('through2'), 8 | uglifycss = require('uglifycss'), 9 | jsmin = require('jsmin2'), 10 | PluginError = require('gulp-util').PluginError, 11 | 12 | gmutil = require('./gmutil'), 13 | store = require('./store') 14 | 15 | 16 | var PLUGIN_NAME = 'gulp-html-inline'; 17 | 18 | var linkRegx = new RegExp('[\\s\\S]*?<*\\/*>*', 'gi'), 19 | hrefRegx = new RegExp('\\s*(href)="+([\\s\\S]*?)"'), 20 | styleRegx = new RegExp('[\\s\\S]*?<\\/style>', 'gi'), 21 | jsRegx = new RegExp('[\\s\\S]*?<\\/script>', 'gi'), 22 | scriptRegx = new RegExp('[\\s\\S]*?<\\/script>', 'gi'), 23 | srcRegx = new RegExp('\\s*(src)="+([\\s\\S]*?)"'); 24 | 25 | var joint = function(tag, content){ 26 | return '<'+ tag +'>' + content + ''; 27 | }; 28 | 29 | // add for gulpman 30 | var _file; 31 | 32 | /** 33 | * 获取get模式下url中的指定参数值 34 | * @param name 参数名 35 | * @param url 传入的url地址 36 | * @returns {*} 37 | */ 38 | var getParams = function(name, url) { 39 | var reg = new RegExp('(^|&)' + name + '=?([^&]*)(&|$)', 'i'), search = ''; 40 | if(url && url !== ''){ 41 | search = (url.split('?')[1] || '').match(reg); 42 | }else{ 43 | search = window.location.search.substr(1).match(reg); 44 | } 45 | if(search && search[0].indexOf(name) !== -1) { 46 | return search[2] ? decodeURI(search[2]) : null; 47 | } 48 | }; 49 | 50 | //压缩内联css代码 | js脚本 51 | var miniInline = function(content, type, options){ 52 | var isMinifyCss = options && !!options.minifyCss, 53 | isMinifyJs = options && !!options.minifyJs, 54 | ignore = options['ignore'] || 'ignore', 55 | basePath = options['basePath'] || '', 56 | queryKey = options['queryKey'] || '_gm_inline', 57 | queryRegx = new RegExp('&*'+ queryKey +'[=|&]?', 'i'), 58 | code = content, 59 | tags; 60 | 61 | tags = content.match(/<[\s\S]*?<*\/*[\s\S]*?>/gi); 62 | 63 | if(tags && tags[0] && tags[0].indexOf(ignore) !== -1) 64 | return content; 65 | 66 | if('css' === type){ 67 | if(!isMinifyCss) return content; 68 | code = uglifycss.processString(content, options); 69 | } 70 | else if('js' === type){ 71 | if(!isMinifyJs) return content; 72 | 73 | /** 74 | * FIX BUGS FOR replace 75 | */ 76 | // gmutil.alert('content: \n'+content) 77 | // gmutil.alert('opts: \n'+JSON.stringify(options)) 78 | 79 | var pt = /(?=['"]?)([\w\/\-\?\&\=]*?\.js)(?=['"]?)/gm, 80 | item 81 | 82 | // 如果没有标记inline,那么不处理 83 | if((item = content.match(pt)) && (item = item[0])){ 84 | if(!item.match(queryRegx)) return content 85 | } 86 | 87 | // gmutil.alert(jsmin(content, options).code) 88 | code = jsmin(content, options).code.replace(/\n*\t*/gi, ''); 89 | } 90 | return code; 91 | }; 92 | 93 | //replace callback src | href 94 | var replaceCallback = function(sourceRegx, match, parentFile, type, options){ 95 | 96 | var ms = sourceRegx.exec(match), 97 | code = '', 98 | query, 99 | isMinifyCss = options && !!options.minifyCss, 100 | isMinifyJs = options && !!options.minifyJs, 101 | ignore = options['ignore'] || 'ignore', 102 | basePath = options['basePath'] || '', 103 | queryKey = options['queryKey'] || '_gm_inline', 104 | queryRegx = new RegExp('&*'+ queryKey +'[=|&]?', 'i'); 105 | 106 | if(!ms || !ms[2] || '' === ms[2]){ 107 | return miniInline(match, type, options); 108 | } 109 | var attr = ms[1] || '', 110 | href = ms[2] || ''; 111 | 112 | 113 | if(match.indexOf(ignore) !== -1) 114 | return match.replace(queryRegx, ''); 115 | 116 | //在url地址上加上 _gm_inline_字段就可以直接嵌入网页 117 | query = getParams(queryKey, href); 118 | 119 | if(query === undefined){ 120 | return match.replace(queryRegx, ''); 121 | } 122 | 123 | // 如果使用绝对路径 124 | if(options['absoluteRoot']){ 125 | var sourceFile = path.join(options['root'], options['absoluteRoot'], href.split('?')[0]) 126 | }else { 127 | var sourceFile = path.normalize(path.dirname(parentFile) + path.sep + basePath + href.split('?')[0]) 128 | } 129 | 130 | if(!options['is_runtime']){ 131 | 132 | var url_runtime_dir = path.join(options['root'], options['runtime_dir']), 133 | url_dist_dir = path.join(options['root'], options['dist_dir']), 134 | sourceFile = sourceFile.replace(url_dist_dir, url_runtime_dir) 135 | } 136 | // remove the url prefix for once 137 | // the url is abs url 138 | sourceFile = sourceFile.replace(options['url_prefix'], '') 139 | 140 | if(!fs.existsSync(sourceFile)){ 141 | gmutil.error('\n*Error: \n*Inline File Not Exist: '+sourceFile+'\n') 142 | 143 | return match; 144 | } 145 | 146 | content = getFileContent(sourceFile); 147 | 148 | // add for gulpman store save 149 | store.save(sourceFile, _file.path) 150 | 151 | 152 | if('css' === type){ 153 | if(!isMinifyCss) 154 | return joint('style', content); 155 | code = uglifycss.processString(content, options); 156 | code = joint('style', code); 157 | } 158 | else if('js' === type){ 159 | 160 | if(!isMinifyJs) 161 | return joint('script', content); 162 | code = jsmin(content, options).code.replace(/\n*\t*/gi, ''); 163 | code = joint('script', code); 164 | } 165 | 166 | return code; 167 | 168 | }; 169 | 170 | //根据标签类型获取内容并压缩 171 | var execture = function(file, options){ 172 | 173 | var parentFile = path.normalize(file.path); 174 | var fileContents = file.contents.toString('utf8'); 175 | if(typeof fileContents === 'undefined'){ 176 | fileContents = getFileContent(file.path); 177 | } 178 | 179 | // get the single tag replace-content (mined) 180 | var content = fileContents 181 | .replace(linkRegx, function($1){ 182 | 183 | //like: 184 | return replaceCallback(hrefRegx, $1, parentFile, 'css', options); 185 | 186 | }).replace(jsRegx, function($1){ 187 | 188 | //like: 189 | return replaceCallback(srcRegx, $1, parentFile, 'js', options); 190 | 191 | }).replace(styleRegx, function($1){ 192 | 193 | //like: 194 | // 200 | //console.log($1); 201 | return miniInline($1, 'css', options); 202 | 203 | }).replace(scriptRegx, function($1){ 204 | //like: 205 | // 212 | 213 | /** 214 | * @debug for replace bug 215 | * this trigger the src with type joined together 216 | * lead into miniInline 217 | */ 218 | return miniInline($1, 'js', options); 219 | }); 220 | 221 | return content; 222 | }; 223 | 224 | //get the content of files 225 | var getFileContent = function(file){ 226 | if(!fs.existsSync(file)) throw new Error('File not find: ' + file); 227 | var fileContent = fs.readFileSync(file, { encoding: 'utf8' }); 228 | return fileContent; 229 | //file.contents = new Buffer(uglifycss.processString(fileContent, options)); 230 | }; 231 | 232 | //get the mined files content 233 | var getContent = function(file, options){ 234 | 235 | var content = execture(file, options); 236 | return content; 237 | }; 238 | 239 | //将压缩后的内容替换到html中 240 | var inline = function(options){ 241 | var options = options || {}, 242 | basePath = options.basePath; 243 | //是否压缩css, 默认压缩 244 | options.minifyCss = 'undefined' === typeof(options.minifyCss) ? true : options.minifyCss; 245 | //是否压缩js, 默认压缩 246 | options.minifyJs = 'undefined' === typeof(options.minifyJs) ? true : options.minifyJs; 247 | 248 | return through2.obj(function(file, enc, next){ 249 | 250 | if (file.isStream()) { 251 | this.emit('error', new PluginError(PLUGIN_NAME, 'Stream content is not supported')); 252 | return next(null, file); 253 | } 254 | 255 | _file = file 256 | 257 | if (file.isBuffer()) { 258 | try { 259 | var content = getContent(file, options); 260 | 261 | file.contents = new Buffer(content); 262 | } 263 | catch (err) { 264 | this.emit('error', new PluginError(PLUGIN_NAME, err['message'])); 265 | } 266 | } 267 | this.push(file); 268 | return next(); 269 | 270 | 271 | }); 272 | 273 | }; 274 | 275 | module.exports = inline; -------------------------------------------------------------------------------- /lib/revReplace.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var path = require('path'); 4 | var gutil = require('gulp-util'); 5 | var through = require('through2'); 6 | 7 | var gmutil = require('./gmutil'); 8 | 9 | 10 | function plugin(options) { 11 | var renames = []; 12 | var cache = []; 13 | 14 | options = options || {}; 15 | 16 | if (!options.canonicalUris) { 17 | options.canonicalUris = true; 18 | } 19 | 20 | options.replaceInExtensions = options.replaceInExtensions || ['.js', '.css', '.html', '.hbs']; 21 | 22 | // @debug add for gulpman 23 | options.prefix = options.prefix || ''; 24 | options['_prefix'] = options['prefix'] 25 | options['_all_prefix'] = new Set() 26 | var _url_prefix = options['url_prefix'] 27 | 28 | // @add prefix array and fn support 29 | // @Lucas 30 | if(typeof options.prefix == 'string'){ 31 | options['_all_prefix'].add(options.prefix) 32 | }else if(options.prefix instanceof Array){ 33 | // 虽然在gulpman/index中已经做了proxy,但是目前注释掉这块,会导致/http://xx的问题,需要处理下 34 | Object.defineProperty(options, 'prefix',{ 35 | get: function () { 36 | var p = this['_prefix'][gmutil.randomNum(0, options['_prefix'].length-1)] 37 | 38 | options['_all_prefix'].add(p) 39 | 40 | return p 41 | }, 42 | set : function (val) { 43 | this['_prefix'] = val 44 | }, 45 | configurable : true 46 | }) 47 | // if the param is a function 48 | }else if(options.prefix instanceof Function){ 49 | Object.defineProperty(options, 'prefix',{ 50 | get: function () { 51 | var p = this['_prefix'](options['_tmpMediaFilePath'], options['_tmpMediaFilePath']) 52 | 53 | options['_all_prefix'].add(p) 54 | 55 | return p; 56 | 57 | }, 58 | set : function (val) { 59 | this['_prefix'] = val 60 | }, 61 | configurable : true 62 | }) 63 | }else { 64 | // default 65 | } 66 | 67 | 68 | return through.obj(function collectRevs(file, enc, cb) { 69 | if (file.isNull()) { 70 | this.push(file); 71 | return cb(); 72 | } 73 | 74 | if (file.isStream()) { 75 | this.emit('error', new gutil.PluginError('gulp-rev-replace', 'Streaming not supported')); 76 | return cb(); 77 | } 78 | 79 | 80 | // 此处file是宿主文件 81 | options['_file'] = file 82 | 83 | // Collect renames from reved files. 84 | if (file.revOrigPath) { 85 | 86 | // @add for gulpman hooks 87 | options['_tmpHostFilePath'] = file.path 88 | 89 | var mediaFilePath = fmtPath(file.revOrigBase, file.revOrigPath) 90 | 91 | // media file ,js css,egg. 92 | options['_tmpMediaFilePath'] = mediaFilePath 93 | 94 | // 此处触发一次运行函数 95 | var availablePrefix = options.prefix 96 | 97 | renames.push({ 98 | unreved: mediaFilePath, 99 | reved: availablePrefix + fmtPath(file.base, file.path) 100 | }); 101 | } 102 | 103 | if (options.replaceInExtensions.indexOf(path.extname(file.path)) > -1) { 104 | // file should be searched for replaces 105 | cache.push(file); 106 | } else { 107 | // nothing to do with this file 108 | this.push(file); 109 | } 110 | 111 | cb(); 112 | }, function replaceInFiles(cb) { 113 | var stream = this; 114 | 115 | if (options.manifest) { 116 | // Read manifest file for the list of renames. 117 | options.manifest.on('data', function (file) { 118 | 119 | var manifest = JSON.parse(file.contents.toString()); 120 | 121 | 122 | Object.keys(manifest).forEach(function (srcFile) { 123 | 124 | // @add for gulpman hooks 125 | var unrevedFilePath = canonicalizeUri(srcFile) 126 | //if want delete source file, can do delete here 127 | options['_tmpMediaFilePath'] = unrevedFilePath 128 | 129 | if(options['_file']){ 130 | options['_tmpHostFilePath'] = options['_file'].path 131 | } 132 | 133 | // 此处每次get prefix,都会运行一次产生函数,每次结果可能不同 134 | var availablePrefix = options.prefix 135 | 136 | renames.push({ 137 | unreved: unrevedFilePath, 138 | reved: availablePrefix + canonicalizeUri(manifest[srcFile]) 139 | }); 140 | 141 | }); 142 | }); 143 | 144 | options.manifest.on('end', replaceContents); 145 | } 146 | else { 147 | replaceContents(); 148 | } 149 | 150 | function replaceContents() { 151 | renames = renames.sort(gmutil.byLongestUnreved); 152 | 153 | // Once we have a full list of renames, search/replace in the cached 154 | // files and push them through. 155 | cache.forEach(function replaceInFile(file) { 156 | var contents = file.contents.toString(); 157 | 158 | renames.forEach(function replaceOnce(rename) { 159 | var unreved = options.modifyUnreved ? options.modifyUnreved(rename.unreved) : rename.unreved; 160 | var reved = options.modifyReved ? options.modifyReved(rename.reved) : rename.reved; 161 | 162 | contents = contents.split(unreved).join(reved); 163 | 164 | options['_all_prefix'].size && options['_all_prefix'].forEach((availablePrefix)=>{ 165 | // add for gulpman hooks 166 | // 这块需要遍历所有prefix,每次prefix可能会变,导致替换失败 167 | if (availablePrefix) { 168 | var _tmp_full_url_prefix = _url_prefix + '/'+ availablePrefix; 169 | var _full_url_prefix = gmutil.joinUrl(availablePrefix, _url_prefix); 170 | 171 | contents = contents.split(_tmp_full_url_prefix).join(_full_url_prefix) 172 | } 173 | }) 174 | 175 | }); 176 | 177 | file.contents = new Buffer(contents); 178 | stream.push(file); 179 | }); 180 | 181 | cb(); 182 | } 183 | }); 184 | 185 | 186 | function fmtPath(base, filePath) { 187 | var newPath = path.relative(base, filePath); 188 | 189 | return canonicalizeUri(newPath); 190 | } 191 | 192 | 193 | // 标准化url 194 | function canonicalizeUri(filePath) { 195 | if (path.sep !== '/' && options.canonicalUris) { 196 | filePath = filePath.split(path.sep).join('/'); 197 | } 198 | 199 | return filePath; 200 | } 201 | 202 | } 203 | 204 | 205 | /** 206 | * Export API 207 | * @type {[type]} 208 | */ 209 | 210 | module.exports = plugin; 211 | -------------------------------------------------------------------------------- /lib/spriter/README.md: -------------------------------------------------------------------------------- 1 | [![npm version](https://badge.fury.io/js/gulp-css-spriter.svg)](http://badge.fury.io/js/gulp-css-spriter) 2 | 3 | # gulp-css-spriter 4 | 5 | `gulp-css-spriter`, a [gulp](http://gulpjs.com/) plugin, looks through the CSS you pipe in and gathers all of the background images. It then creates a sprite sheet and updates the references in the CSS. 6 | 7 | You can easily exclude/include certain background image declarations using meta info in your styles([*see meta section below*](#meta-options)) and `includeMode` option([*see options section below*](#options)) depending on your use case. 8 | 9 | # Install 10 | 11 | ### Latest Version: 0.3.3 12 | 13 | `npm install gulp-css-spriter` 14 | 15 | # About 16 | 17 | `gulp-css-spriter` uses [spritesmith](https://www.npmjs.com/package/spritesmith) behind the scenes for creating the sprite sheet. 18 | 19 | # Usage 20 | 21 | ## Basic usage 22 | 23 | This is most likely the setup you will probably end up using. 24 | 25 | ``` 26 | var gulp = require('gulp'); 27 | var spriter = require('gulp-css-spriter'); 28 | 29 | gulp.task('css', function() { 30 | return gulp.src('./src/css/styles.css') 31 | .pipe(spriter({ 32 | // The path and file name of where we will save the sprite sheet 33 | 'spriteSheet': './dist/images/spritesheet.png', 34 | // Because we don't know where you will end up saving the CSS file at this point in the pipe, 35 | // we need a litle help identifying where it will be. 36 | 'pathToSpriteSheetFromCSS': '../images/spritesheet.png' 37 | })) 38 | .pipe(gulp.dest('./dist/css')); 39 | }); 40 | ``` 41 | 42 | ## Barebones usage 43 | 44 | The slimmest usage possible. 45 | 46 | ``` 47 | var gulp = require('gulp'); 48 | var spriter = require('gulp-css-spriter'); 49 | 50 | gulp.task('css', function() { 51 | return gulp.src('./styles.css') 52 | .pipe(spriter()) 53 | .pipe(gulp.dest('./')); 54 | }); 55 | ``` 56 | 57 | ## Minify CSS output usage 58 | 59 | If you want to use [@meta data](#meta-options) but are using a preprocessor such as Sass or Less, you will need to use a output style that doesn't strip comments. After piping the CSS through `gulp-css-spriter`, you can then run it through a CSS minifier(separate plugin), such as [`gulp-minify-css`](https://www.npmjs.com/package/gulp-minify-css). 60 | 61 | ``` 62 | var gulp = require('gulp'); 63 | var spriter = require('gulp-css-spriter'); 64 | var minifyCSS = require('gulp-minify-css'); // https://www.npmjs.com/package/gulp-minify-css 65 | 66 | gulp.task('css', function() { 67 | return gulp.src('./styles.css') 68 | .pipe(spriter()) 69 | .pipe(minifyCSS()) 70 | .pipe(gulp.dest('./')); 71 | }); 72 | ``` 73 | 74 | # Options 75 | 76 | - `options`: object - hash of options 77 | - `includeMode`: string - Determines whether meta data is necessary or not 78 | - Values: 'implicit', 'explicit' 79 | - Default: 'implicit' 80 | - For example, if `explicit`, you must have meta `include` as `true` in order for the image declarations to be included in the spritesheet: `/* @meta {"spritesheet": {"include": true}} */` 81 | - If left default at `implicit`, all images will be included in the spritesheet; except for image declarations with meta `include` as `false`: `/* @meta {"spritesheet": {"include": false}} */` 82 | - `spriteSheet`: string - The path and file name of where we will save the sprite sheet 83 | - Default: 'spritesheet.png' 84 | - `pathToSpriteSheetFromCSS`: string - Because we don't know where you will end up saving the CSS file at this point in the pipe, we need a litle help identifying where it will be. We will use this as the reference to the sprite sheet image in the CSS piped in. 85 | - Default: 'spritesheet.png' 86 | - `spriteSheetBuildCallback`: function - Same as the [spritesmith callback](https://www.npmjs.com/package/spritesmith#-spritesmith-params-callback-) 87 | - Default: null 88 | - Callback has a parameters as so: `function(err, result)` 89 | - `result.image`: Binary string representation of image 90 | - `result.coordinates`: Object mapping filename to {x, y, width, height} of image 91 | - `result.properties`: Object with metadata about spritesheet {width, height} 92 | - `silent`: bool - We ignore any images that are not found but are supposed to be sprited by default 93 | - Default: true 94 | - `shouldVerifyImagesExist`: bool - Check to make sure each image declared in the CSS exists before passing it to the spriter. Although silenced by default(`options.silent`), if an image is not found, an error is thrown. 95 | - Default: true 96 | - `spritesmithOptions`: object - Any option you pass in here, will be passed through to spritesmith. [See spritesmith options documenation](https://www.npmjs.com/package/spritesmith#-spritesmith-params-callback-) 97 | - Default: {} 98 | - `outputIndent`: bool - Used to format output CSS. You should be using a separate beautifier plugin. The reason the output code is reformatted is because it is easier to "parse->stringify" than "replace in place". 99 | - Default: '\t' 100 | 101 | 102 | # What we emit 103 | 104 | `gulp-css-spriter` emits the transformed CSS with updated image references to the sprite sheet as a normal Gulp [vinyl file](https://www.npmjs.com/package/vinyl). 105 | 106 | We also attach the binary sprite sheet image in `chunk.spriteSheet` in case you want to consume it later down the pipe. 107 | 108 | 109 | # Meta info 110 | 111 | `gulp-css-spriter` uses a JSON format to add info onto CSS declarations. 112 | 113 | The example below will exclude this declaration from the spritesheet. 114 | ``` 115 | /* @meta {"spritesheet": {"include": false}} */ 116 | background: url('../images/dummy-blue.png'); 117 | ``` 118 | 119 | Please note that if you are compiling from Sass/Less and are not getting correct results, to check the outputted CSS and make sure the comments are still in tact and on the line you expect. For Sass, use multiline `/* */` comment syntax and put them above declarations. This is because gulp-sass/node-sass/libsass removes single line comments and puts mult-line comments that are on the same line as a declaration, below the declaraton. 120 | 121 | The `@meta` comment data can be above or on the same line as the declaration for it to apply. 122 | ``` 123 | /* @meta {"spritesheet": {"include": false}} */ 124 | background: url('../images/dummy-blue.png'); /* @meta {"spritesheet": {"include": false}} */ 125 | ``` 126 | 127 | ## Meta options 128 | 129 | - `spritesheet`: object - hash of options that `gulp-css-spriter` will factor in 130 | - `include`: bool - determines whether or not the declaration should be included in the spritesheet. This can be left undefined if the `includeMode` is 'implicit' 131 | 132 | 133 | 134 | # What we emit 135 | 136 | `gulp-css-spriter` transforms your CSS image paths to the spritesheet appropriately then emits the CSS as a normal Gulp [vinyl file](https://www.npmjs.com/package/vinyl). 137 | 138 | - Gulp [vinyl file](https://www.npmjs.com/package/vinyl). We emit the CSS you passed in with transformed image paths 139 | 140 | ## Events 141 | 142 | ### `.on('log', function(message) { })` 143 | 144 | We emit log messages such as when a image defined in the CSS can't be found on disk. 145 | 146 | ### `.on('error', function(err) { })` 147 | 148 | A normal gulp error. There are a variety of errors. See source code for more details. 149 | 150 | 151 | 152 | # Testing 153 | 154 | We have a series of unit tests. We use [Mocha](http://mochajs.org/). 155 | 156 | Install Mocha globally: 157 | ``` 158 | npm install -g mocha 159 | ``` 160 | 161 | Run tests with: `mocha` or `npm test` 162 | -------------------------------------------------------------------------------- /lib/spriter/index.js: -------------------------------------------------------------------------------- 1 | // gulp-css-spriter: https://www.npmjs.com/package/gulp-css-spriter 2 | // Sprite Sheet Generation from CSS source files. 3 | // 4 | // By: Eric Eastwood: EricEastwood.com 5 | // 6 | // Meta info looks like: `/* @meta {"spritesheet": {"include": false}} */` 7 | 8 | var fs = require('fs-extra'); 9 | var path = require('path'); 10 | 11 | var Promise = require('bluebird'); 12 | var outputFile = Promise.promisify(fs.outputFile); 13 | var stat = Promise.promisify(fs.stat); 14 | 15 | var through = require('through2'); 16 | var extend = require('extend') 17 | var gutil = require('gulp-util'); 18 | 19 | var css = require('css'); 20 | var spritesmith = require('spritesmith'); 21 | var spritesmithBuild = Promise.promisify(spritesmith); 22 | 23 | 24 | var spriterUtil = require('./lib/spriter-util'); 25 | var getBackgroundImageDeclarations = require('./lib/get-background-image-declarations'); 26 | var transformFileWithSpriteSheetData = require('./lib/transform-file-with-sprite-sheet-data'); 27 | 28 | 29 | var gmutil = require('../gmutil'), 30 | store = require('../store') 31 | 32 | 33 | // consts 34 | const PLUGIN_NAME = 'gulp-css-spriter'; 35 | const gm_sprite_img_dir = 'gm_sprite_img' 36 | const gm_sprite_subfix = '_sprite_sheet.png' 37 | 38 | 39 | var spriter = function(options) { 40 | 41 | var defaults = { 42 | // ('implicit'|'explicit') 43 | 'includeMode': 'implicit', 44 | // The path and file name of where we will save the sprite sheet 45 | 'spriteSheet': 'spritesheet.png', 46 | // Because we don't know where you will end up saving the CSS file at this point in the pipe, 47 | // we need a litle help identifying where it will be. 48 | 'pathToSpriteSheetFromCSS': 'spritesheet.png', 49 | // Same as the spritesmith callback `function(err, result)` 50 | // result.image: Binary string representation of image 51 | // result.coordinates: Object mapping filename to {x, y, width, height} of image 52 | // result.properties: Object with metadata about spritesheet {width, height} 53 | 'spriteSheetBuildCallback': null, 54 | // If true, we ignore any images that are not found on disk 55 | // Note: this plugin will still emit an error if you do not verify that the images exist 56 | 'silent': true, 57 | // Check to make sure each image declared in the CSS exists before passing it to the spriter. 58 | // Although silenced by default(`options.silent`), if an image is not found, an error is thrown. 59 | 'shouldVerifyImagesExist': true, 60 | // Any option you pass in here, will be passed through to spritesmith 61 | // https://www.npmjs.com/package/spritesmith#-spritesmith-params-callback- 62 | 'spritesmithOptions': {}, 63 | // Used to format output CSS 64 | // You should be using a separate beautifier plugin 65 | 'outputIndent': '\t' 66 | }; 67 | 68 | var settings = extend({}, defaults, options); 69 | 70 | // Keep track of all the chunks that come in so that we can re-emit in the flush 71 | var chunkList = []; 72 | // We use an object for imageMap so we don't get any duplicates 73 | var imageMap = {}; 74 | // Check to make sure all of the images exist(`options.shouldVerifyImagesExist`) before trying to sprite them 75 | var imagePromiseArray = []; 76 | 77 | // var currentChunkFile; 78 | 79 | var stream = through.obj(function(chunk, enc, cb) { 80 | // http://nodejs.org/docs/latest/api/stream.html#stream_transform_transform_chunk_encoding_callback 81 | //console.log('transform'); 82 | 83 | // Each `chunk` is a vinyl file: https://www.npmjs.com/package/vinyl 84 | // chunk.cwd 85 | // chunk.base 86 | // chunk.path 87 | // chunk.contents 88 | var self = this; 89 | 90 | if (chunk.isStream()) { 91 | self.emit('error', new gutil.PluginError(PLUGIN_NAME, 'Cannot operate on stream')); 92 | } 93 | else if (chunk.isBuffer()) { 94 | var contents = String(chunk.contents); 95 | 96 | var styles; 97 | try { 98 | styles = css.parse(contents, { 99 | 'silent': settings.silent, 100 | 'source': chunk.path 101 | }); 102 | } 103 | catch(err) { 104 | err.message = 'Something went wrong when parsing the CSS: ' + err.message; 105 | self.emit('log', err.message); 106 | 107 | // Emit an error if necessary 108 | if(!settings.silent) { 109 | self.emit('error', err); 110 | } 111 | } 112 | 113 | 114 | var fileDir = path.relative(settings['dist_root'], chunk.base) 115 | 116 | 117 | var fileName = path.basename(chunk.relative) 118 | 119 | // add for gulpman 120 | chunk.base = settings['cwd'] 121 | // currentChunkFile = chunk 122 | 123 | var relDistPath = path.join( 124 | gm_sprite_img_dir, 125 | fileName+gm_sprite_subfix 126 | ) 127 | 128 | var distSpritePath = path.join(settings['dist_root'], fileDir, relDistPath) 129 | 130 | settings['spriteSheet'] = distSpritePath 131 | 132 | settings['pathToSpriteSheetFromCSS'] = relDistPath 133 | 134 | // Gather a list of all of the image declarations 135 | var chunkBackgroundImageDeclarations = getBackgroundImageDeclarations(styles, settings.includeMode); 136 | 137 | 138 | // Go through each declaration and gather the image paths 139 | // We find the new images that we found in this chunk verify they exist below 140 | // We use an object so we don't get any duplicates 141 | var newImagesfFromChunkMap = {}; 142 | var backgroundURLMatchAllRegex = new RegExp(spriterUtil.backgroundURLRegex.source, "gi"); 143 | chunkBackgroundImageDeclarations.forEach(function(declaration) { 144 | 145 | // Match each background image in the declaration (there could be multiple background images per value) 146 | spriterUtil.matchBackgroundImages(declaration.value, function(imagePath) { 147 | imagePath = path.join(path.dirname(chunk.path), imagePath); 148 | 149 | // If not already in the overall list of images collected 150 | // Add to the queue/list of images to be verified 151 | if(!imageMap[imagePath]) { 152 | newImagesfFromChunkMap[imagePath] = true; 153 | } 154 | 155 | // Add it to the main overall list to keep track 156 | imageMap[imagePath] = true; 157 | }); 158 | }); 159 | 160 | // Filter out any images that do not exist depending on `settings.shouldVerifyImagesExist` 161 | Object.keys(newImagesfFromChunkMap).forEach(function(imagePath) { 162 | var filePromise; 163 | if(settings.shouldVerifyImagesExist) { 164 | filePromise = stat(imagePath).then(function() { 165 | return { 166 | doesExist: true, 167 | path: imagePath 168 | }; 169 | }, function() { 170 | return { 171 | doesExist: false, 172 | path: imagePath 173 | }; 174 | }); 175 | } 176 | else { 177 | // If they don't want us to verify it exists, just pass it on with a undefined `doesExist` property 178 | filePromise = Promise.resolve({ 179 | doesExist: undefined, 180 | path: imagePath 181 | }); 182 | } 183 | 184 | imagePromiseArray.push(filePromise); 185 | }); 186 | 187 | // Keep track of each chunk and what declarations go with it 188 | // Because the positions/line numbers pertain to that chunk only 189 | chunkList.push(chunk); 190 | 191 | } 192 | 193 | 194 | // "call callback when the transform operation is complete." 195 | cb(); 196 | 197 | }, function(cb) { 198 | // http://nodejs.org/docs/latest/api/stream.html#stream_transform_flush_callback 199 | //console.log('flush'); 200 | var self = this; 201 | 202 | // @debug 203 | 204 | // Create an verified image list when all of the async checks have finished 205 | var imagesVerifiedPromise = Promise.settle(imagePromiseArray).then(function(results) { 206 | var imageList = []; 207 | Array.prototype.forEach.call(results, function(result) { 208 | imageInfo = result.value(); 209 | 210 | if(imageInfo.doesExist === true || imageInfo.doesExist === undefined) { 211 | imageList.push(imageInfo.path); 212 | } 213 | else { 214 | 215 | // Tell them that we could not find the image 216 | var logMessage = 'Image could not be found: ' + imageInfo.path; 217 | self.emit('log', logMessage); 218 | 219 | // Emit an error if necessary 220 | if(!settings.silent) { 221 | self.emit('error', { 222 | 'name': 'NotFound', 223 | 'message': logMessage, 224 | 'plugin': PLUGIN_NAME 225 | }); 226 | } 227 | } 228 | }); 229 | 230 | return imageList; 231 | }); 232 | 233 | 234 | // Start spriting once we know the true list of images that exist 235 | imagesVerifiedPromise.then(function(imageList) { 236 | 237 | // fix the invalid png files when no-sprite-img 238 | // @debug ..可能有问题。。 239 | if(!imageList.length) { 240 | chunkList.forEach(chunk=>{ 241 | self.push(chunk) 242 | }) 243 | 244 | return cb(); 245 | } 246 | 247 | 248 | 249 | // Generate the spritesheet 250 | var spritesmithOptions = extend({}, settings.spritesmithOptions, { src: imageList }); 251 | 252 | var spriteSmithBuildPromise = spritesmithBuild(spritesmithOptions); 253 | 254 | spriteSmithBuildPromise.then(function(result) { 255 | 256 | var whenImageDealtWithPromise = new Promise(function(resolve, reject) { 257 | // Save out the spritesheet image 258 | if(settings.spriteSheet) { 259 | var spriteSheetSavedPromise = outputFile(settings.spriteSheet, result.image, 'binary').then(function() { 260 | 261 | //console.log("The file was saved!"); 262 | 263 | // Push all of the chunks back on the pipe 264 | chunkList.forEach(function(chunk) { 265 | 266 | var transformedChunk = chunk.clone(); 267 | 268 | // gmutil.alert('IMG: '+imageList) 269 | // save into relevancy store 270 | store.save(imageList, chunk.path, 'sprite') 271 | 272 | 273 | try { 274 | transformedChunk = transformFileWithSpriteSheetData(transformedChunk, result.coordinates, settings.pathToSpriteSheetFromCSS, settings.includeMode, settings.silent, settings.outputIndent); 275 | } 276 | catch(err) { 277 | err.message = 'Something went wrong when transforming chunks: ' + err.message; 278 | self.emit('log', err.message); 279 | 280 | // Emit an error if necessary 281 | if(!settings.silent) { 282 | self.emit('error', err); 283 | } 284 | 285 | reject(err); 286 | } 287 | 288 | 289 | // Attach the spritesheet in case someone wants to use it down the pipe 290 | transformedChunk.spritesheet = result.image; 291 | 292 | // Push it back on the main pipe 293 | self.push(transformedChunk); 294 | }); 295 | 296 | 297 | }, function() { 298 | settings.spriteSheetBuildCallback(err, null); 299 | reject(err); 300 | }); 301 | 302 | 303 | spriteSheetSavedPromise.finally(function() { 304 | 305 | // Call a callback from the settings the user can hook onto 306 | if(settings.spriteSheetBuildCallback) { 307 | settings.spriteSheetBuildCallback(null, result); 308 | } 309 | 310 | resolve(); 311 | }); 312 | } 313 | else { 314 | resolve(); 315 | } 316 | }); 317 | 318 | whenImageDealtWithPromise.finally(function() { 319 | // "call callback when the flush operation is complete." 320 | cb(); 321 | }); 322 | 323 | 324 | }, function(err) { 325 | if(err) { 326 | err.message = 'Error creating sprite sheet image:\n' + err.message; 327 | self.emit('error', new gutil.PluginError(PLUGIN_NAME, err)); 328 | } 329 | }); 330 | 331 | 332 | }); 333 | 334 | 335 | 336 | 337 | 338 | }); 339 | 340 | // returning the file stream 341 | return stream; 342 | }; 343 | 344 | 345 | module.exports = spriter; -------------------------------------------------------------------------------- /lib/spriter/lib/get-background-image-declarations.js: -------------------------------------------------------------------------------- 1 | 2 | var mapOverStylesAndTransformBackgroundImageDeclarations = require('./map-over-styles-and-transform-background-image-declarations'); 3 | 4 | 5 | // Pass in a styles object from `css.parse` 6 | // See main module for `includeMode` values 7 | function getBackgroundImageDeclarations(styles, includeMode) { 8 | includeMode = includeMode || 'implicit'; 9 | 10 | // First get all of the background image declarations 11 | var backgroundImageDeclarations = []; 12 | mapOverStylesAndTransformBackgroundImageDeclarations(styles, includeMode, function(declaration) { 13 | backgroundImageDeclarations.push(declaration); 14 | }); 15 | 16 | return backgroundImageDeclarations; 17 | } 18 | 19 | 20 | 21 | 22 | 23 | module.exports = getBackgroundImageDeclarations; -------------------------------------------------------------------------------- /lib/spriter/lib/get-meta-info-for-declaration.js: -------------------------------------------------------------------------------- 1 | var extend = require('extend'); 2 | 3 | 4 | function getMetaInfoForDeclaration(declarations, declarationIndex) { 5 | var resultantMetaData = {}; 6 | 7 | if(declarationIndex > 0 && declarationIndex < declarations.length) { 8 | var mainDeclaration = declarations[declarationIndex]; 9 | if(mainDeclaration) { 10 | 11 | // Meta data can exist before or on the same line as the declaration. 12 | // Both Meta blocks are valid for the background property 13 | // ex. 14 | // /* @meta {"spritesheet": {"include": false}} */ 15 | // background: url('../images/aenean-purple.png'); /* @meta {"sprite": {"skip": true}} */ 16 | var beforeDeclaration = declarations[declarationIndex-1]; 17 | var afterDeclaration = declarations[declarationIndex+1]; 18 | 19 | 20 | if(beforeDeclaration) { 21 | // The before declaration should be valid no matter what (even if multiple lines above) 22 | // The parse function does all the nice checking for us 23 | extend(resultantMetaData, parseCommentDecarationForMeta(beforeDeclaration)); 24 | } 25 | 26 | if(afterDeclaration) { 27 | //console.log(mainDeclaration); 28 | //console.log(afterDeclaration); 29 | //console.log(afterDeclaration.position.start.line, mainDeclaration.position.start.line); 30 | // Make sure that the comment starts on the same line as the main declaration 31 | if((((afterDeclaration || {}).position || {}).start || {}).line === (((mainDeclaration || {}).position || {}).start || {}).line) { 32 | extend(resultantMetaData, parseCommentDecarationForMeta(afterDeclaration)); 33 | } 34 | } 35 | } 36 | } 37 | 38 | 39 | return resultantMetaData; 40 | } 41 | 42 | function parseCommentDecarationForMeta(declaration) { 43 | if(declaration.type === "comment") { 44 | //console.log(declaration); 45 | 46 | var metaMatches = declaration.comment.match(/@meta\s*({.*?}(?!}))/); 47 | 48 | if(metaMatches) { 49 | var parsedMeta = {}; 50 | try { 51 | parsedMeta = JSON.parse(metaMatches[1]); 52 | } 53 | catch(e) { 54 | //console.warn('Meta info was found but failed was not valid JSON'); 55 | } 56 | 57 | return parsedMeta; 58 | } 59 | } 60 | } 61 | 62 | 63 | 64 | module.exports = getMetaInfoForDeclaration; -------------------------------------------------------------------------------- /lib/spriter/lib/map-over-styles-and-transform-background-image-declarations.js: -------------------------------------------------------------------------------- 1 | var extend = require('extend'); 2 | 3 | var spriterUtil = require('./spriter-util'); 4 | var getMetaInfoForDeclaration = require('./get-meta-info-for-declaration'); 5 | var transformMap = require('./transform-map'); 6 | 7 | 8 | 9 | function mapOverStylesAndTransformBackgroundImageDeclarations(styles, includeMode, cb) { 10 | // Map over all 11 | return mapOverStylesAndTransformAllBackgroundImageDeclarations(styles, function(declaration) { 12 | // Then filter down to only the proper ones (according to their meta data) 13 | if(shouldIncludeFactoringInMetaData(declaration.meta, includeMode)) { 14 | return cb.apply(null, arguments); 15 | } 16 | }); 17 | } 18 | 19 | // Boolean function to determine if the meta data permits using this declaration 20 | function shouldIncludeFactoringInMetaData(meta, includeMode) { 21 | var metaIncludeValue = (meta && meta.spritesheet && meta.spritesheet.include); 22 | var shouldIncludeBecauseImplicit = includeMode === 'implicit' && (metaIncludeValue === undefined || metaIncludeValue); 23 | var shouldIncludeBecauseExplicit = includeMode === 'explicit' && metaIncludeValue; 24 | var shouldInclude = shouldIncludeBecauseImplicit || shouldIncludeBecauseExplicit; 25 | 26 | // Only return declartions that shouldn't be skipped 27 | return shouldInclude; 28 | } 29 | 30 | 31 | 32 | // Pass in a styles object from `css.parse` 33 | // Loop over all of the styles and transform/modify the background image declarations 34 | // Returns a new styles object that has the transformed declarations 35 | function mapOverStylesAndTransformAllBackgroundImageDeclarations(styles, cb) { 36 | // Clone the declartion to keep it immutable 37 | var transformedStyles = extend(true, {}, styles); 38 | 39 | // Go over each background `url()` declarations 40 | transformedStyles.stylesheet.rules.map(function(rule, ruleIndex) { 41 | if(rule.type === 'rule') { 42 | 43 | rule.declarations = transformMap(rule.declarations, function(declaration, declarationIndex, declarations) { 44 | // Clone the declartion to keep it immutable 45 | var transformedDeclaration = extend(true, {}, declaration); 46 | transformedDeclaration = attachInfoToDeclaration(declarations, declarationIndex); 47 | 48 | /*// background-image always has a url 49 | if(transformedDeclaration.property === 'background-image') { 50 | return cb(transformedDeclaration, declarationIndex, declarations); 51 | } 52 | // Background is a shorthand property so make sure `url()` is in there 53 | else if(transformedDeclaration.property === 'background') { 54 | var hasImageValue = spriterUtil.backgroundURLRegex.test(transformedDeclaration.value); 55 | 56 | if(hasImageValue) { 57 | return cb(transformedDeclaration, declarationIndex, declarations); 58 | } 59 | }*/ 60 | 61 | 62 | // background-image always has a url且 63 | // 判断url是否有?__spriter后缀 64 | var gm_sprite_reg = /\?_gm_sprite/i, 65 | gm_sprite_str = '?_gm_sprite'; 66 | 67 | if (transformedDeclaration.property === 'background-image' && gm_sprite_reg.test(transformedDeclaration.value)) { 68 | 69 | transformedDeclaration.value = transformedDeclaration.value.replace(gm_sprite_str, ''); 70 | 71 | return cb(transformedDeclaration, declarationIndex, declarations); 72 | } 73 | //Background is a short hand property so make sure `url()` is in there 74 | // 且判断url是否有?__spriter后缀 75 | else if(transformedDeclaration.property === 'background' && gm_sprite_reg.test(transformedDeclaration.value)) { 76 | 77 | transformedDeclaration.value = transformedDeclaration.value.replace(gm_sprite_str, ''); 78 | 79 | var hasImageValue = spriterUtil.backgroundURLRegex.test(transformedDeclaration.value); 80 | 81 | if (hasImageValue) { 82 | return cb(transformedDeclaration, declarationIndex, declarations); 83 | } 84 | } 85 | 86 | // Wrap in an object so that the declaration doesn't get interpreted 87 | return { 88 | 'value': transformedDeclaration 89 | }; 90 | }); 91 | 92 | } 93 | 94 | return rule; 95 | }); 96 | 97 | return transformedStyles; 98 | } 99 | 100 | // We do NOT directly modify the declaration in the rule 101 | // We pass the whole rule and current index so we can properly look at the metaData around each declaration 102 | // and add it to the declaration 103 | function attachInfoToDeclaration(declarations, declarationIndex) 104 | { 105 | if(declarations.length > declarationIndex) { 106 | // Clone the declartion to keep it immutable 107 | var declaration = extend(true, {}, declarations[declarationIndex]); 108 | 109 | var declarationMetaInfo = getMetaInfoForDeclaration(declarations, declarationIndex); 110 | 111 | // Add the meta into to the declaration 112 | declaration.meta = extend(true, {}, declaration.meta, declarationMetaInfo); 113 | 114 | return declaration; 115 | } 116 | 117 | return null; 118 | } 119 | 120 | 121 | 122 | module.exports = mapOverStylesAndTransformBackgroundImageDeclarations; 123 | -------------------------------------------------------------------------------- /lib/spriter/lib/spriter-util.js: -------------------------------------------------------------------------------- 1 | 2 | var backgroundURLRegex = (/(.*?url\(["\']?)(.*?\.(?:png|jpg|gif))(["\']?\).*?;?)/i); 3 | 4 | 5 | function matchBackgroundImages(declarationValue, cb) { 6 | var backgroundURLMatchAllRegex = new RegExp(backgroundURLRegex.source, "gi"); 7 | 8 | return declarationValue.replace(backgroundURLMatchAllRegex, function(match, p1, p2, p3, offset, string) { 9 | var imagePath = p2; 10 | 11 | return p1 + cb(imagePath) + p3; 12 | }); 13 | } 14 | 15 | 16 | 17 | module.exports = { 18 | 'backgroundURLRegex': backgroundURLRegex, 19 | 'matchBackgroundImages':matchBackgroundImages 20 | }; 21 | -------------------------------------------------------------------------------- /lib/spriter/lib/transform-file-with-sprite-sheet-data.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var extend = require('extend'); 3 | 4 | var css = require('css'); 5 | 6 | var spriterUtil = require('./spriter-util'); 7 | var mapOverStylesAndTransformBackgroundImageDeclarations = require('./map-over-styles-and-transform-background-image-declarations'); 8 | 9 | var backgroundURLMatchAllRegex = new RegExp(spriterUtil.backgroundURLRegex.source, "gi"); 10 | 11 | 12 | // Replace all the paths that need replacing 13 | function transformFileWithSpriteSheetData(vinylFile, coordinateMap, pathToSpriteSheetFromCSS, /*optional*/includeMode, /*optional*/isSilent, /*optional*/outputIndent) { 14 | includeMode = includeMode ? includeMode : 'implicit'; 15 | isSilent = (isSilent !== undefined) ? isSilent : false; 16 | outputIndent = outputIndent ? outputIndent : '\t'; 17 | 18 | // Clone the declartion to keep it immutable 19 | var resultantFile = vinylFile.clone(); 20 | 21 | if(resultantFile) { 22 | 23 | var styles = css.parse(String(resultantFile.contents), { 24 | 'silent': isSilent, 25 | 'source': vinylFile.path 26 | }); 27 | 28 | styles = mapOverStylesAndTransformBackgroundImageDeclarations(styles, includeMode, function(declaration) { 29 | 30 | var coordList = []; 31 | declaration.value = spriterUtil.matchBackgroundImages(declaration.value, function(imagePath) { 32 | 33 | var coords = coordinateMap[path.join(path.dirname(resultantFile.path), imagePath)]; 34 | //console.log('coords', coords); 35 | 36 | // Make sure there are coords for this image in the sprite sheet, otherwise we won't include it 37 | if(coords) { 38 | coordList.push("-" + coords.x + "px -" + coords.y + "px"); 39 | 40 | // If there are coords in the spritemap for this image, lets use the spritemap 41 | return pathToSpriteSheetFromCSS; 42 | } 43 | 44 | return imagePath; 45 | }); 46 | 47 | return { 48 | 'value': declaration, 49 | /* */ 50 | // Add the appropriate background position according to the spritemap 51 | 'insertElements': (function() { 52 | if(coordList.length > 0) { 53 | return { 54 | type: 'declaration', 55 | property: 'background-position', 56 | value: coordList.join(', ') 57 | }; 58 | } 59 | })() 60 | /* */ 61 | }; 62 | }); 63 | 64 | //console.log(styles.stylesheet.rules[0].declarations); 65 | 66 | // Put it back into string form 67 | var resultantContents = css.stringify(styles, { 68 | indent: outputIndent 69 | }); 70 | //console.log(resultantContents); 71 | resultantFile.contents = new Buffer(resultantContents); 72 | } 73 | 74 | return resultantFile; 75 | } 76 | 77 | module.exports = transformFileWithSpriteSheetData; -------------------------------------------------------------------------------- /lib/spriter/lib/transform-map.js: -------------------------------------------------------------------------------- 1 | var extend = require('extend'); 2 | 3 | function transformMap(arr, cb) { 4 | var resultantArray = extend(true, [], arr); 5 | 6 | for(var i = 0; i < resultantArray.length; i++) { 7 | var el = resultantArray[i]; 8 | 9 | var result = cb(el, i, resultantArray); 10 | 11 | var defaults = { 12 | value: el, 13 | insertElements: [], 14 | appendElements: [] 15 | }; 16 | 17 | // You can pass in a bare value or as the `value` property of an object 18 | result = typeof result === 'object' ? result : { value: result }; 19 | // Massage the result into shape 20 | result = extend({}, defaults, result); 21 | 22 | 23 | // Transform the current value 24 | resultantArray[i] = result.value ? result.value : result; 25 | 26 | // Insert after the current element 27 | var insertElements = [].concat(result.insertElements); 28 | if(insertElements.length > 0) { 29 | Array.prototype.splice.apply(resultantArray, [i+1, 0].concat(insertElements)); 30 | } 31 | 32 | // Add the elements onto the end 33 | var appendElements = [].concat(result.appendElements); 34 | if(appendElements.length > 0) { 35 | resultantArray = resultantArray.concat(appendElements); 36 | } 37 | } 38 | 39 | return resultantArray; 40 | } 41 | 42 | 43 | module.exports = transformMap; -------------------------------------------------------------------------------- /lib/spriter/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gulp-css-spriter", 3 | "version": "0.3.3", 4 | "description": "Sprite Sheet Generation from CSS source files. The best and different approach to sprite sheets.", 5 | "main": "index.js", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/MadLittleMods/gulp-css-spriter.git" 9 | }, 10 | "keywords": [ 11 | "css", 12 | "gulp", 13 | "gulpplugin", 14 | "gulpfriendly", 15 | "sprite", 16 | "spritesheet", 17 | "sass", 18 | "less" 19 | ], 20 | "author": { 21 | "name": "Eric Eastwood", 22 | "email": "contact@ericeastwood.com", 23 | "url": "http://ericeastwood.com/" 24 | }, 25 | "license": "MIT", 26 | "bugs": { 27 | "url": "https://github.com/MadLittleMods/gulp-css-spriter/issues" 28 | }, 29 | "scripts": { 30 | "test": "mocha" 31 | }, 32 | "dependencies": { 33 | "bluebird": "^2.9.3", 34 | "css": "^2.1.0", 35 | "extend": "^2.0.0", 36 | "fs-extra": "^0.14.0", 37 | "gulp-util": "^3.0.1", 38 | "spritesmith": "^1.0.3", 39 | "through2": "^0.6.2" 40 | }, 41 | "devDependencies": { 42 | "chai": "^1.10.0", 43 | "chai-as-promised": "^4.1.1", 44 | "gulp": "^3.8.10", 45 | "mocha": "^2.1.0" 46 | }, 47 | "gitHead": "2a4f030a00e0135ff8270acf1f34bb0c2537d102", 48 | "homepage": "https://github.com/MadLittleMods/gulp-css-spriter", 49 | "_id": "gulp-css-spriter@0.3.3", 50 | "_shasum": "4f69315ac01d92a0dc9996c02054cbd4826aeac1", 51 | "_from": "gulp-css-spriter@latest", 52 | "_npmVersion": "1.4.23", 53 | "_npmUser": { 54 | "name": "mlm", 55 | "email": "contact@ericeastwood.com" 56 | }, 57 | "maintainers": [ 58 | { 59 | "name": "mlm", 60 | "email": "contact@ericeastwood.com" 61 | } 62 | ], 63 | "dist": { 64 | "shasum": "4f69315ac01d92a0dc9996c02054cbd4826aeac1", 65 | "tarball": "http://registry.npmjs.org/gulp-css-spriter/-/gulp-css-spriter-0.3.3.tgz" 66 | }, 67 | "directories": {}, 68 | "_resolved": "https://registry.npmjs.org/gulp-css-spriter/-/gulp-css-spriter-0.3.3.tgz" 69 | } 70 | -------------------------------------------------------------------------------- /lib/store.js: -------------------------------------------------------------------------------- 1 | /** 2 | * For create JSON file 3 | */ 4 | 5 | var fs = require('fs'), 6 | path = require('path'), 7 | j = path.join 8 | 9 | 10 | var gmutil = require('./gmutil') 11 | 12 | var _cwd = process.cwd() 13 | 14 | // var jsonFileName = 'gm-relevancy.json', 15 | // jsonFilePath 16 | 17 | var dataBase = { 18 | 'name':'gm-relevancy', 19 | 'time': (new Date()).getTime(), 20 | 'source': {}, 21 | 'sprite': {} 22 | // 'requiredCSS': {}, 23 | } 24 | 25 | 26 | function initStore(db) { 27 | 28 | // check global namespace 29 | if(!global['gm_ns']) { 30 | global['gm_ns'] = {} 31 | } 32 | 33 | // update dataBase from global is existed 34 | if(global['gm_ns']['data_base']){ 35 | dataBase = db || global['gm_ns']['data_base'] 36 | 37 | }else{ 38 | 39 | global['gm_ns']['data_base'] = db || dataBase 40 | 41 | } 42 | 43 | } 44 | 45 | 46 | function _write(fpath){ 47 | fs.writeFileSync(fpath, JSON.stringify(dataBase, null, 4)) 48 | } 49 | 50 | function _push (sourceFile, refFile, dbType) { 51 | // 如果已经存储,那么不再重复push 52 | if(_isInList(sourceFile, refFile, dbType)) return true 53 | 54 | !dataBase[dbType] && (dataBase[dbType] = {}) 55 | if(dataBase[dbType][sourceFile]){ 56 | dataBase[dbType][sourceFile].push(refFile) 57 | }else { 58 | dataBase[dbType][sourceFile] = [refFile] 59 | } 60 | } 61 | 62 | function _check(sourceAbsPath, handler, dbType, handlerType, evtObj){ 63 | 64 | // gmutil.alert('Store: \n') 65 | // console.log(global['gm_ns']['data_base']) 66 | 67 | var dbType = dbType || 'source' 68 | 69 | if(sourceAbsPath in dataBase[dbType]){ 70 | // gmutil.alert('HIT Store: \n') 71 | // console.log(dataBase) 72 | 73 | if(handler){ 74 | dataBase[dbType][sourceAbsPath].forEach(item=>{ 75 | 76 | gmutil.warn('*Relevancy Process: '+item) 77 | // the handler default as file type 78 | var hfn = handlerType || path.extname(item).slice(1) 79 | 80 | handler[hfn] && handler[hfn](item, evtObj) 81 | 82 | }) 83 | } 84 | 85 | return true 86 | 87 | }else { 88 | 89 | return false 90 | } 91 | 92 | 93 | } 94 | 95 | 96 | function _isInList(sourceFile, refFile, dbType){ 97 | var r 98 | return (dataBase[dbType]) && (r = dataBase[dbType][sourceFile]) && r.indexOf(refFile) !== -1 99 | } 100 | 101 | 102 | 103 | function _save (sourceFile, refFile, dbType) { 104 | var dbType = dbType || 'source' 105 | 106 | // gmutil.alert(sourceFile+' :\n'+refFile) 107 | if(typeof sourceFile == 'string'){ 108 | // just save in memory now, not write in file 109 | return _push(sourceFile, refFile, dbType) 110 | }else if(gmutil.isArray(sourceFile)){ 111 | sourceFile.forEach((e,i)=>{ 112 | _push(e, refFile, dbType) 113 | }) 114 | 115 | return true; 116 | }else { 117 | gmutil.error("*Relevancy Store Error:\n") 118 | throw Error('Unknown Type: '+sourceFile) 119 | } 120 | 121 | } 122 | 123 | 124 | function getStore(){ 125 | return dataBase 126 | } 127 | 128 | 129 | // init store 130 | initStore() 131 | 132 | 133 | // api 134 | exports.save = _save 135 | exports.check = _check 136 | 137 | exports.get = getStore 138 | 139 | 140 | -------------------------------------------------------------------------------- /meta/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xunuoi/gulpman/d12eb0fa1c71a955f98d2c4bb7090540a518dd7a/meta/favicon.ico -------------------------------------------------------------------------------- /meta/home/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xunuoi/gulpman/d12eb0fa1c71a955f98d2c4bb7090540a518dd7a/meta/home/img/logo.png -------------------------------------------------------------------------------- /meta/home/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | HOME PAGE 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 |
16 |

Welcome to Gulpman!

17 |

18 | Now You Can Create Your Own Modular Front-end Build System Easily! 19 |

20 |
21 | 22 |
23 |

More Information

24 | 33 | 34 |

35 |
36 | 37 | 38 |
39 |

How to use ReactJS in Gulpman ?

40 | 41 |
    42 |
  1. Put ReactJS file in html by script tag.
  2. 43 |
  3. such as: 44 |

    45 | <script src="react.min.js" type="text/javascript"></script> 46 |
    47 | <script src="react-dom.min.js" type="text/javascript"></script> 48 |

    49 |
  4. 50 |
  5. Or Import React in Your ES6/JSX: 51 |

    52 | import 'react' 53 |

    54 |
  6. 55 |
  7. Then you can directly use the React and ReactDOM Object in your ES6/JSX files.
  8. 56 |
57 | 58 |

Example:

59 |
60 |

Waiting ...

61 |
62 |
63 | 64 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /meta/home/main.es6: -------------------------------------------------------------------------------- 1 | /** 2 | * FOR HOME 3 | */ 4 | 5 | import 'react' 6 | 7 | let HelloMessage = React.createClass({ 8 | render () { 9 | return
The Content Are Created By {this.props.name}
10 | } 11 | }) 12 | 13 | 14 | 15 | function renderHello (argument) { 16 | ReactDOM.render( 17 | , 18 | document.getElementById('demo1') 19 | ) 20 | } 21 | 22 | 23 | 24 | setTimeout(()=>{ 25 | 26 | renderHello() 27 | 28 | }, 1200) 29 | 30 | 31 | export { 32 | renderHello 33 | } -------------------------------------------------------------------------------- /meta/home/main.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * FOR HOME 3 | */ 4 | 5 | 6 | .logo { 7 | background: url(./img/logo.png) no-repeat; 8 | } 9 | 10 | // for sprite demo 11 | .demo-sprite { 12 | background: url(./img/logo.png?_gm_sprite) no-repeat; 13 | width: 50px; 14 | height: 50px; 15 | } 16 | 17 | 18 | .demo-inline { 19 | // if you want use inline-base64, 20 | // add `?_gm_inline` after img url 21 | } 22 | 23 | .header { 24 | width: 600px; 25 | margin: 0 auto; 26 | text-align: center; 27 | 28 | p { 29 | text-align: center; 30 | } 31 | } 32 | 33 | .more-info { 34 | margin-top: 20px; 35 | border: 1px dashed #8EB139; 36 | padding: 20px; 37 | background-color: #FFAA89; 38 | border-radius: 8px; 39 | 40 | h3 { 41 | color: white; 42 | } 43 | a { 44 | text-decoration: none; 45 | } 46 | ul li { 47 | line-height: 30px; 48 | } 49 | } 50 | 51 | .demo-container { 52 | margin-top: 30px; 53 | border: 1px dashed #8EB139; 54 | padding: 20px; 55 | background-color: #5B9EB2; 56 | border-radius: 8px; 57 | 58 | ol li p { 59 | color: #14581F; 60 | text-shadow: 2px 2px 2px #9A9A9A; 61 | } 62 | 63 | h3 { 64 | color: white; 65 | } 66 | 67 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gulpman", 3 | "version": "2.0.0", 4 | "description": "Create Modular Front-End Build System. Based on gulp, very easy and light", 5 | "engines": { 6 | "node": ">=4.0.0" 7 | }, 8 | "main": "index.js", 9 | "scripts": { 10 | "test": "make test" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "git+https://github.com/xunuoi/gulpman.git" 15 | }, 16 | "keywords": [ 17 | "gulp", 18 | "modular", 19 | "gulpman", 20 | "scss", 21 | "es6", 22 | "react", 23 | "jsx", 24 | "build", 25 | "easy", 26 | "fis", 27 | "component" 28 | ], 29 | "author": "Lucas X", 30 | "license": "MIT", 31 | "bugs": { 32 | "url": "https://github.com/xunuoi/gulpman/issues" 33 | }, 34 | "homepage": "https://github.com/xunuoi/gulpman#readme", 35 | "devDependencies": { 36 | "gulp": "^3.9.0" 37 | }, 38 | "dependencies": { 39 | "gulp-babel": "^6.1.2", 40 | "browserify": "^13.1.0", 41 | "stringify": "^5.1.0", 42 | "colors": "^1.1.2", 43 | "event-stream": "^3.3.2", 44 | "globby": "^6.0.0", 45 | "gulp-cssnano": "^2.1.2", 46 | "gulp-imagemin": "^3.0.2", 47 | "gulp-load-plugins": "^1.2.4", 48 | "gulp-concat": "^2.6.0", 49 | "gulp-rename": "^1.2.2", 50 | "gulp-rev-all": "^0.9.7", 51 | "gulp-sequence": "^0.4.4", 52 | "gulp-uglify": "^2.0.0", 53 | "gulp-sourcemaps": "^1.6.0", 54 | "imagemin-pngquant": "^5.0.0", 55 | "readable-stream": "^2.0.5", 56 | "object-assign": "^4.0.1", 57 | "shelljs": "^0.5.3", 58 | "through2": "^2.0.1", 59 | "vinyl-buffer": "^1.0.0", 60 | "vinyl-source-stream": "^1.1.0", 61 | "gulp-util": "^3.0.7", 62 | "jsmin2": "^1.2.1", 63 | "uglifycss": "0.0.20", 64 | "bluebird": "^3.4.1", 65 | "css": "^2.2.1", 66 | "extend": "^3.0.0", 67 | "fs-extra": "^0.30.0", 68 | "spritesmith": "^2.0.1" 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /presetlib/react-0.14.6/build/react-dom-server.js: -------------------------------------------------------------------------------- 1 | /** 2 | * ReactDOMServer v0.14.6 3 | * 4 | * Copyright 2013-2015, Facebook, Inc. 5 | * All rights reserved. 6 | * 7 | * This source code is licensed under the BSD-style license found in the 8 | * LICENSE file in the root directory of this source tree. An additional grant 9 | * of patent rights can be found in the PATENTS file in the same directory. 10 | * 11 | */ 12 | // Based off https://github.com/ForbesLindesay/umd/blob/master/template.js 13 | ;(function(f) { 14 | // CommonJS 15 | if (typeof exports === "object" && typeof module !== "undefined") { 16 | module.exports = f(require('react')); 17 | 18 | // RequireJS 19 | } else if (typeof define === "function" && define.amd) { 20 | define(['react'], f); 21 | 22 | //