├── .gitignore ├── .npmignore ├── test ├── src │ ├── img │ │ ├── word.png │ │ ├── excel.png │ │ ├── word@2x.png │ │ └── excel@2x.png │ ├── slice │ │ ├── bg@2x.png │ │ ├── excel.png │ │ ├── word.png │ │ ├── word@2x.png │ │ ├── excel@2x.png │ │ ├── img │ │ │ ├── excel.png │ │ │ ├── word.png │ │ │ ├── excel@2x.png │ │ │ └── word@2x.png │ │ ├── keyframe1@2x.png │ │ └── keyframe2@2x.png │ └── css │ │ └── style.css └── lazyimagecss.js ├── .travis.yml ├── appveyor.yml ├── LICENSE ├── package.json ├── README-zh_CN.md ├── README.md ├── index.js └── lib └── fastimagesize.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .idea -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .idea/ -------------------------------------------------------------------------------- /test/src/img/word.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weixin/gulp-lazyimagecss/HEAD/test/src/img/word.png -------------------------------------------------------------------------------- /test/src/img/excel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weixin/gulp-lazyimagecss/HEAD/test/src/img/excel.png -------------------------------------------------------------------------------- /test/src/img/word@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weixin/gulp-lazyimagecss/HEAD/test/src/img/word@2x.png -------------------------------------------------------------------------------- /test/src/slice/bg@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weixin/gulp-lazyimagecss/HEAD/test/src/slice/bg@2x.png -------------------------------------------------------------------------------- /test/src/slice/excel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weixin/gulp-lazyimagecss/HEAD/test/src/slice/excel.png -------------------------------------------------------------------------------- /test/src/slice/word.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weixin/gulp-lazyimagecss/HEAD/test/src/slice/word.png -------------------------------------------------------------------------------- /test/src/img/excel@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weixin/gulp-lazyimagecss/HEAD/test/src/img/excel@2x.png -------------------------------------------------------------------------------- /test/src/slice/word@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weixin/gulp-lazyimagecss/HEAD/test/src/slice/word@2x.png -------------------------------------------------------------------------------- /test/src/slice/excel@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weixin/gulp-lazyimagecss/HEAD/test/src/slice/excel@2x.png -------------------------------------------------------------------------------- /test/src/slice/img/excel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weixin/gulp-lazyimagecss/HEAD/test/src/slice/img/excel.png -------------------------------------------------------------------------------- /test/src/slice/img/word.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weixin/gulp-lazyimagecss/HEAD/test/src/slice/img/word.png -------------------------------------------------------------------------------- /test/src/slice/img/excel@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weixin/gulp-lazyimagecss/HEAD/test/src/slice/img/excel@2x.png -------------------------------------------------------------------------------- /test/src/slice/img/word@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weixin/gulp-lazyimagecss/HEAD/test/src/slice/img/word@2x.png -------------------------------------------------------------------------------- /test/src/slice/keyframe1@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weixin/gulp-lazyimagecss/HEAD/test/src/slice/keyframe1@2x.png -------------------------------------------------------------------------------- /test/src/slice/keyframe2@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weixin/gulp-lazyimagecss/HEAD/test/src/slice/keyframe2@2x.png -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: node_js 3 | node_js: 4 | - "0.10" 5 | - "0.12" 6 | - "stable" 7 | after_script: 8 | - npm test 9 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | # AppVeyor file 2 | # http://www.appveyor.com/docs/lang/nodejs-iojs 3 | # http://www.appveyor.com/docs/appveyor-yml 4 | 5 | version: "{build}" 6 | 7 | clone_depth: 10 8 | 9 | environment: 10 | matrix: 11 | - nodejs_version: 0.12 12 | - nodejs_version: 4 13 | 14 | # Install scripts. (runs after repo cloning) 15 | install: 16 | # Get the latest stable version of Node.js or io.js 17 | - ps: Install-Product node $env:nodejs_version 18 | # install modules 19 | - npm install 20 | 21 | # Post-install test scripts. 22 | test_script: 23 | # Output useful info for debugging. 24 | - node --version && npm --version 25 | # run tests 26 | - npm test 27 | 28 | # Don't actually build. 29 | build: off 30 | 31 | matrix: 32 | fast_finish: true 33 | 34 | cache: 35 | - C:\Users\appveyor\AppData\Roaming\npm-cache -> package.json # npm cache 36 | - node_modules -> package.json # local npm modules -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 TmT Team and other contributors 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gulp-lazyimagecss", 3 | "version": "2.0.8", 4 | "dependencies": { 5 | "async": "^1.5.0", 6 | "css": "^2.2.1", 7 | "lodash": "^3.10.1", 8 | "through2": "^2.0.0", 9 | "vinyl": "^1.1.0" 10 | }, 11 | "scripts": { 12 | "test": "mocha" 13 | }, 14 | "description": "Be lazy, add images' CSS automatically, like width & height and more.", 15 | "main": "index.js", 16 | "repository": { 17 | "type": "git", 18 | "url": "https://github.com/weixin/gulp-lazyimagecss" 19 | }, 20 | "keywords": [ 21 | "gulpplugin", 22 | "lazy", 23 | "image", 24 | "CSS", 25 | "background-size" 26 | ], 27 | "author": { 28 | "name": "littledu", 29 | "email": "410491325@qq.com" 30 | }, 31 | "license": "MIT", 32 | "bugs": { 33 | "url": "https://github.com/weixin/gulp-lazyimagecssissues" 34 | }, 35 | "homepage": "https://github.com/weixin/gulp-lazyimagecss", 36 | "readmeFilename": "README.md", 37 | "devDependencies": { 38 | "mocha": "~2.3.4", 39 | "through2": "^2.0.0", 40 | "vinyl-fs": "^2.2.1", 41 | "should": "^8.0.2" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /README-zh_CN.md: -------------------------------------------------------------------------------- 1 | ## gulp-lazyimagecss [![NPM Version](http://img.shields.io/npm/v/gulp-lazyimagecss.svg?style=flat)](https://www.npmjs.com/package/gulp-lazyimagecss "Package version") 2 | 3 | [![Build Status](https://travis-ci.org/weixin/node-lwip.svg)](https://travis-ci.org/weixin/gulp-lazyimagecss "Build Status") 4 | [![Win Build status](https://img.shields.io/appveyor/ci/hzlzh/gulp-lazyimagecss.svg?label=Win%20build)](https://ci.appveyor.com/project/hzlzh/gulp-lazyimagecss) 5 | [![dependencies](https://img.shields.io/david/weixin/gulp-lazyimagecss.svg)](https://ci.appveyor.com/project/weixin/gulp-lazyimagecss "Dependencies") 6 | [![NPM Downloads](https://img.shields.io/npm/dm/gulp-lazyimagecss.svg?style=flat)](https://www.npmjs.com/package/gulp-lazyimagecss "NPM Downloads") 7 | 8 | [![Join the chat at https://gitter.im/weixin/gulp-lazyimagecss](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/TmT?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 9 | [![TmT Name](https://img.shields.io/badge/Team-TmT-brightgreen.svg?style=flat)](https://github.com/orgs/TmT/people "Tencent Moe Team") 10 | [![License](https://img.shields.io/npm/l/gulp-lazyimagecss.svg?style=flat)](http://opensource.org/licenses/MIT "Feel free to contribute.") 11 | 12 | > 高效地书写 CSS 必备,自动生成图片`CSS`属性,如:`width` & `height` 等。 13 | > 省时省力,懒人必备! 14 | 15 | **NPM 官方主页:** [https://www.npmjs.com/package/gulp-lazyimagecss](https://www.npmjs.com/package/gulp-lazyimagecss) 16 | 17 | ## 安装 18 | 19 | 通过 [NPM](https://npmjs.org/) 安装*(也可使用 [CNPM](http://cnpmjs.org/) 等源)* 20 | 21 | ```javascript 22 | npm install gulp-lazyimagecss --save 23 | ``` 24 | 25 | ## 使用 26 | 27 | 配置 **gulpfile.js** 28 | 29 | ```javascript 30 | var lazyimagecss = require('gulp-lazyimagecss'); 31 | 32 | gulp.src(paths.src.less) 33 | .pipe(less()) 34 | .pipe(lazyimagecss()) 35 | .pipe(gulp.dest(paths.src.css)); 36 | 37 | ``` 38 | 39 | **配置选项** 40 | 设置希望被自动生成的 CSS 属性 41 | 设置生效的图片目录 42 | 43 | ```javascript 44 | options = lodash.extend({ 45 | width: true, // 生成 CSS: width 46 | height: true, // 生成 CSS: height 47 | backgroundSize: true, // 生成 CSS: background-size 48 | imagePath: [] // 设置图片生效目录(数组格式,如:`['../slice','../img']`) 49 | }, options); 50 | ``` 51 | 52 | ## 效果 53 | 54 | **CSS 输入** 55 | 56 | 57 | ```css 58 | .bg-test { 59 | background: url(../img/bg.png); 60 | background-repeat: no-repeat; 61 | } 62 | .icon-test { 63 | background-image: url(../slice/test.png); 64 | } 65 | .icon-test-retina { 66 | background-image: url(../slice/test@2x.png); 67 | } 68 | ``` 69 | 70 | **CSS 输出** 71 | 72 | ```css 73 | .bg-test { 74 | background: url(../img/bg.png); 75 | background-repeat: no-repeat; 76 | width: 1100px; 77 | height: 300px; 78 | } 79 | .icon-test { 80 | background-image: url(../slice/test.png); // test.png 原图片尺寸为 32x32 81 | width: 32px; 82 | height: 32px; 83 | } 84 | .icon-test-retina { 85 | background-image: url(../slice/test@2x.png); // test@2x.png 原图片尺寸为 64x64 86 | width: 32px; 87 | height: 32px; 88 | background-size: 32px; 89 | } 90 | ``` 91 | 92 | _提示: 输出 CSS 可配合使用 [PostCSS](https://github.com/postcss/postcss) 进一步处理_ 93 | 94 | ## 说明 95 | 96 | * 如果 `width` / `height` / `background-size` 等属性已存在,则不会覆盖**对应的**原始属性值。 97 | * 使用 `background-image: url()` 或 `background: url()` 均可被正确识别。 98 | * 通过读取图片 `HEX` 数据取得图片 宽/高 信息,大大提升相应速度,参看:[fast-image-size](https://github.com/Ziv-Barber/fast-image-size)。 99 | * 通过 [file signatures](https://en.wikipedia.org/wiki/List_of_file_signatures) 判断检测图片类别,如:`PNG` & `JPG`。 100 | * 去掉图片最小 `buffer size` 的限制,参看:[/fast-image-size/pull/5](https://github.com/Ziv-Barber/fast-image-size/pull/5)。 101 | 102 | ## 参与贡献 103 | 104 | 此项目由 [TmT 团队](https://github.com/orgs/TmT/people) 创建和维护。 105 | 如果你有 `Bug反馈` 或 `功能建议`,请创建 [Issue](https://github.com/weixin/gulp-lazyimagecss/issues) 或发送 [Pull Request](https://github.com/weixin/gulp-lazyimagecss/pulls) 给我们,感谢你的参与和贡献。 -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## gulp-lazyimagecss [![NPM Version](http://img.shields.io/npm/v/gulp-lazyimagecss.svg?style=flat)](https://www.npmjs.com/package/gulp-lazyimagecss "Package version") 2 | 3 | [![Build Status](https://travis-ci.org/weixin/node-lwip.svg)](https://travis-ci.org/weixin/gulp-lazyimagecss "Build Status") 4 | [![Win Build status](https://img.shields.io/appveyor/ci/hzlzh/gulp-lazyimagecss.svg?label=Win%20build)](https://ci.appveyor.com/project/hzlzh/gulp-lazyimagecss) 5 | [![dependencies](https://img.shields.io/david/weixin/gulp-lazyimagecss.svg)](https://ci.appveyor.com/project/weixin/gulp-lazyimagecss "devDependencies") 6 | [![NPM Downloads](https://img.shields.io/npm/dm/gulp-lazyimagecss.svg?style=flat)](https://www.npmjs.com/package/gulp-lazyimagecss "NPM Downloads") 7 | 8 | [![Join the chat at https://gitter.im/weixin/gulp-lazyimagecss](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/TmT?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 9 | [![TmT Name](https://img.shields.io/badge/Team-TmT-brightgreen.svg?style=flat)](https://github.com/orgs/TmT/people "Tencent Moe Team") 10 | [![License](https://img.shields.io/npm/l/gulp-lazyimagecss.svg?style=flat)](http://opensource.org/licenses/MIT "Feel free to contribute.") 11 | 12 | > Be lazy, add images's CSS automatically, like `width` & `height` and more. 13 | > Save time, make money. 14 | 15 | **NPM Home Page:** [https://www.npmjs.com/package/gulp-lazyimagecss](https://www.npmjs.com/package/gulp-lazyimagecss) 16 | 17 | *[中文(zh_CN) 说明文档 →](https://github.com/weixin/gulp-lazyimagecss/blob/master/README-zh_CN.md)* 18 | 19 | ## Install 20 | 21 | Install with [NPM](https://npmjs.org/): 22 | 23 | ```javascript 24 | npm install gulp-lazyimagecss --save 25 | ``` 26 | 27 | ## Usage 28 | 29 | **gulpfile.js** 30 | 31 | ```javascript 32 | var lazyimagecss = require('gulp-lazyimagecss'); 33 | 34 | gulp.src(paths.src.less) 35 | .pipe(less()) 36 | .pipe(lazyimagecss()) 37 | .pipe(gulp.dest(paths.src.css)); 38 | 39 | ``` 40 | 41 | **Options** 42 | Set CSS which you wish to be added automatically. 43 | 44 | ```javascript 45 | options = lodash.extend({ 46 | width: true, // Output width in CSS 47 | height: true, // Output height in CSS 48 | backgroundSize: true, // Output background-size in CSS 49 | imagePath: [] // Set image path to be worked (e.g. `['../slice','../img']`) 50 | }, options); 51 | ``` 52 | 53 | ## Result 54 | 55 | **CSS In** 56 | 57 | 58 | ```css 59 | .bg-test { 60 | background: url(../img/bg.png); 61 | background-repeat: no-repeat; 62 | } 63 | .icon-test { 64 | background-image: url(../slice/test.png); 65 | } 66 | .icon-test-retina { 67 | background-image: url(../slice/test@2x.png); 68 | } 69 | ``` 70 | 71 | **CSS Out** 72 | 73 | ```css 74 | .bg-test { 75 | background: url(../img/bg.png); 76 | background-repeat: no-repeat; 77 | width: 1100px; 78 | height: 300px; 79 | } 80 | .icon-test { 81 | background-image: url(../slice/test.png); // test.png - 32x32 82 | width: 32px; 83 | height: 32px; 84 | } 85 | .icon-test-retina { 86 | background-image: url(../slice/test@2x.png); // test@2x.png - 64x64 87 | width: 32px; 88 | height: 32px; 89 | background-size: 32px; 90 | } 91 | ``` 92 | 93 | _Tips: Use [PostCSS](https://github.com/postcss/postcss) with `CSS Out` if needed._ 94 | 95 | ## Notes 96 | 97 | * CSS property generating will be ignored if any of those `width` / `height` / `background-size` already set. 98 | * Both `background-image: url()` and `background: url()` can be detected successfully. 99 | * Get image size from `HEX` data from file buffer via [fast-image-size](https://github.com/Ziv-Barber/fast-image-size), more fast now. 100 | * Detect `PNG` & `JPG` based on [file signatures](https://en.wikipedia.org/wiki/List_of_file_signatures). 101 | * No minimum image buffer size limited now [/fast-image-size/pull/5](https://github.com/Ziv-Barber/fast-image-size/pull/5). 102 | 103 | ## Contributing 104 | 105 | This repo is build and maintained by [TmT Team](https://github.com/orgs/TmT/people). 106 | If you get any bugs or feature requests, please open a new [Issue](https://github.com/weixin/gulp-lazyimagecss/issues) or send us [Pull Request](https://github.com/weixin/gulp-lazyimagecss/pulls), Thank you for your contributions. -------------------------------------------------------------------------------- /test/src/css/style.css: -------------------------------------------------------------------------------- 1 | .bg{ 2 | width: 100%; 3 | background: url(../slice/bg@2x.png) no-repeat center center; 4 | } 5 | 6 | .background-color{ 7 | background: #000; 8 | } 9 | 10 | .base64{ 11 | background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADcAAAA3CAYAAAHf3OLQAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA+1pVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNS1jMDIxIDc5LjE1NTc3MiwgMjAxNC8wMS8xMy0xOTo0NDowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIiB4bWxuczpzdFJlZj0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlUmVmIyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxNCAoTWFjaW50b3NoKSIgeG1wOkNyZWF0ZURhdGU9IjIwMTUtMDQtMjFUMDg6MjI6NTArMDg6MDAiIHhtcDpNb2RpZnlEYXRlPSIyMDE1LTA0LTIxVDA4OjI4OjAzKzA4OjAwIiB4bXA6TWV0YWRhdGFEYXRlPSIyMDE1LTA0LTIxVDA4OjI4OjAzKzA4OjAwIiBkYzpmb3JtYXQ9ImltYWdlL3BuZyIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpBMDdDMUFFN0UwMjAxMUU0QkRBOTkxRkY2QTE3MEZGMyIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDpBMDdDMUFFOEUwMjAxMUU0QkRBOTkxRkY2QTE3MEZGMyI+IDx4bXBNTTpEZXJpdmVkRnJvbSBz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); 12 | } 13 | 14 | .excel-retina{ 15 | background-image: url(../img/excel@2x.png); 16 | -webkit-background-size: cover; 17 | } 18 | 19 | .word{ 20 | background-image: url(../slice/img/word.png); 21 | } 22 | 23 | .word-retina{ 24 | background-image: url(../slice/word@2x.png); 25 | } 26 | 27 | @media (-webkit-min-device-pixel-ratio:2), (min-device-pixel-ratio: 2){ 28 | .word{ 29 | background-image: url(../slice/img/word.png); 30 | } 31 | 32 | .word-retina{ 33 | background-image: url(../slice/word@2x.png); 34 | } 35 | } 36 | 37 | @keyframes anim { 38 | 0%{ 39 | background-image: url(../slice/keyframe1@2x.png); 40 | } 41 | 100%{ 42 | background-image: url(../slice/keyframe2@2x.png); 43 | } 44 | } -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | var path = require('path'); 3 | var File = require('vinyl'); 4 | var async = require('async'); 5 | var _ = require('lodash'); 6 | var through = require('through2'); 7 | var fastImageSize = require('./lib/fastimagesize'); 8 | var css = require('css'); 9 | 10 | function lazyImageCSS(options) { 11 | 12 | options = _.extend({ 13 | width: true, 14 | height: true, 15 | backgroundSize: true, 16 | imagePath: [] 17 | }, options); 18 | 19 | return through.obj(function (file, enc, cb) { 20 | 21 | var cssContent = file.contents.toString(); 22 | 23 | var imagePath = options.imagePath; 24 | 25 | if (imagePath.length) { 26 | imagePath = '(' + options.imagePath.join('|') + '/)'; 27 | imagePath = imagePath.replace(/\./g, '\\.'); 28 | } else { 29 | imagePath = ''; 30 | } 31 | 32 | var imageRegex = new RegExp('url\\(["\']?(' + imagePath + '[^)]*?)["\']?\\)'); 33 | 34 | var obj = css.parse(cssContent); 35 | 36 | function setProperty(property, value) { 37 | return { 38 | "type": "declaration", 39 | "property": property, 40 | "value": value 41 | }; 42 | } 43 | 44 | function parseRules(rules) { 45 | 46 | for (var i = 0; i < rules.length; i++) { 47 | 48 | var rule = rules[i]; 49 | 50 | if (rule.type === 'keyframes') { 51 | parseRules(rule.keyframes); 52 | } 53 | 54 | if (rule.type === 'media') { 55 | parseRules(rule.rules); 56 | } 57 | 58 | if (rule.type === 'rule' || rule.type === 'keyframe') { 59 | 60 | var declarations = rule.declarations; 61 | var code = {}; 62 | 63 | declarations.forEach(function (declaration) { 64 | code[declaration.property] = declaration.value; 65 | }); 66 | 67 | var property; 68 | var hasImage = false; 69 | 70 | if (code['background-image']) { 71 | property = 'background-image'; 72 | hasImage = true; 73 | } else if (code['background']) { 74 | property = 'background'; 75 | hasImage = true; 76 | } 77 | 78 | if (!hasImage) { 79 | continue; 80 | } 81 | 82 | var value = code[property]; 83 | 84 | var matchValue = imageRegex.exec(value); 85 | 86 | if (!matchValue || matchValue[1].indexOf('data:') === 0) { 87 | continue; 88 | } 89 | 90 | var relativePath = matchValue[1]; 91 | var absolutePath = path.join(path.dirname(file.path), relativePath); 92 | 93 | if (value.indexOf('@2x') > -1) { 94 | options.retina = true; 95 | } else { 96 | options.retina = false; 97 | } 98 | 99 | var info = fastImageSize(absolutePath); 100 | 101 | if (info.type === 'unknown') { 102 | console.log('' + 'unknown type: ' + absolutePath); 103 | continue; 104 | } 105 | 106 | var width, height, newDeclaration; 107 | 108 | if (options.retina) { 109 | width = info.width / 2 + 'px'; 110 | height = info.height / 2 + 'px'; 111 | } else { 112 | width = info.width + 'px'; 113 | height = info.height + 'px'; 114 | } 115 | 116 | if (options.width && !code['width']) { 117 | newDeclaration = setProperty('width', width); 118 | declarations.push(newDeclaration); 119 | } 120 | 121 | if (options.height && !code['height']) { 122 | newDeclaration = setProperty('height', height); 123 | declarations.push(newDeclaration); 124 | } 125 | 126 | if (options.backgroundSize && (options.retina || info.type == 'svg') && !code['background-size'] && !code['-webkit-background-size']) { 127 | newDeclaration = newDeclaration = setProperty('background-size', width + ' ' + height); 128 | declarations.push(newDeclaration); 129 | } 130 | } 131 | } 132 | } 133 | 134 | if (obj.stylesheet.rules.length > 0) { 135 | parseRules(obj.stylesheet.rules); 136 | cssContent = css.stringify(obj); 137 | } 138 | 139 | this.push(new File({ 140 | base: file.base, 141 | path: file.path, 142 | contents: new Buffer(cssContent) 143 | })); 144 | 145 | cb(); 146 | }); 147 | 148 | } 149 | 150 | // Exporting the plugin main function 151 | module.exports = lazyImageCSS; 152 | -------------------------------------------------------------------------------- /test/lazyimagecss.js: -------------------------------------------------------------------------------- 1 | var should = require('should'); 2 | var vfs = require('vinyl-fs'); 3 | var through2 = require('through2'); 4 | var css = require('css'); 5 | var noop = function () {}; 6 | 7 | var lazyimagecss = require('../'); 8 | 9 | //vfs.src('./src/css/style.css') 10 | // .pipe(lazyimagecss()) 11 | // .pipe(vfs.dest('./src/ouput')) 12 | // .on('data', noop); 13 | 14 | describe('lazyimagecss test', function() { 15 | it('[Generated] Image `width`', function(done) { 16 | 17 | vfs.src('./test/src/css/style.css') 18 | .pipe(lazyimagecss()) 19 | .pipe(through2.obj(function(file, enc, cb){ 20 | content = file.contents.toString(); 21 | 22 | content.match(/width/g).length.should.equal(8); 23 | 24 | cb(); 25 | })) 26 | .on('data', noop) 27 | .on('end', done); 28 | 29 | }); 30 | 31 | it('[Correct] `width` value', function(done) { 32 | 33 | vfs.src('./test/src/css/style.css') 34 | .pipe(lazyimagecss()) 35 | .pipe(through2.obj(function(file, enc, cb){ 36 | content = file.contents.toString(); 37 | 38 | content.indexOf('width: 64px').should.be.above(0); 39 | 40 | cb(); 41 | })) 42 | .on('data', noop) 43 | .on('end', done); 44 | 45 | }); 46 | 47 | it('[Generated] Image `height` ', function(done) { 48 | 49 | vfs.src('./test/src/css/style.css') 50 | .pipe(lazyimagecss()) 51 | .pipe(through2.obj(function(file, enc, cb){ 52 | content = file.contents.toString(); 53 | 54 | content.match(/height/g).length.should.equal(8); 55 | 56 | cb(); 57 | })) 58 | .on('data', noop) 59 | .on('end', done); 60 | 61 | }); 62 | 63 | it('[Correct] `height` value', function(done) { 64 | 65 | vfs.src('./test/src/css/style.css') 66 | .pipe(lazyimagecss()) 67 | .pipe(through2.obj(function(file, enc, cb){ 68 | content = file.contents.toString(); 69 | 70 | content.indexOf('height: 64px').should.be.above(0); 71 | 72 | cb(); 73 | })) 74 | .on('data', noop) 75 | .on('end', done); 76 | 77 | }); 78 | 79 | it('[Generated] Image `background-size`', function(done) { 80 | 81 | vfs.src('./test/src/css/style.css') 82 | .pipe(lazyimagecss()) 83 | .pipe(through2.obj(function(file, enc, cb){ 84 | content = file.contents.toString(); 85 | 86 | content.match(/background-size/g).length.should.equal(6); 87 | 88 | cb(); 89 | })) 90 | .on('data', noop) 91 | .on('end', done); 92 | 93 | }); 94 | 95 | it('[Correct] `background-size` value', function(done) { 96 | 97 | vfs.src('./test/src/css/style.css') 98 | .pipe(lazyimagecss()) 99 | .pipe(through2.obj(function(file, enc, cb){ 100 | content = file.contents.toString(); 101 | 102 | content.indexOf('background-size: 64px').should.be.above(0); 103 | 104 | cb(); 105 | })) 106 | .on('data', noop) 107 | .on('end', done); 108 | 109 | }); 110 | 111 | it('[Worked] Option: `width`', function(done) { 112 | 113 | vfs.src('./test/src/css/style.css') 114 | .pipe(lazyimagecss({ 115 | width: false 116 | })) 117 | .pipe(through2.obj(function(file, enc, cb){ 118 | content = file.contents.toString(); 119 | 120 | content.match(/width/g).length.should.equal(1); 121 | 122 | cb(); 123 | })) 124 | .on('data', noop) 125 | .on('end', done); 126 | 127 | }); 128 | 129 | it('[Worked] Option: `height`', function(done) { 130 | 131 | vfs.src('./test/src/css/style.css') 132 | .pipe(lazyimagecss({ 133 | height: false 134 | })) 135 | .pipe(through2.obj(function(file, enc, cb){ 136 | content = file.contents.toString(); 137 | 138 | content.indexOf('height').should.equal(-1); 139 | 140 | cb(); 141 | })) 142 | .on('data', noop) 143 | .on('end', done); 144 | 145 | }); 146 | 147 | it('[Worked] Option: `backgroundSize`', function(done) { 148 | 149 | vfs.src('./test/src/css/style.css') 150 | .pipe(lazyimagecss({ 151 | backgroundSize: false 152 | })) 153 | .pipe(through2.obj(function(file, enc, cb){ 154 | content = file.contents.toString(); 155 | 156 | content.match(/background-size/g).length.should.equal(1); 157 | 158 | cb(); 159 | })) 160 | .on('data', noop) 161 | .on('end', done); 162 | 163 | }); 164 | 165 | it('[Worked] Option: `imagePath`', function(done) { 166 | 167 | vfs.src('./test/src/css/style.css') 168 | .pipe(lazyimagecss({ 169 | imagePath: ['../img'] 170 | })) 171 | .pipe(through2.obj(function(file, enc, cb){ 172 | content = file.contents.toString(); 173 | 174 | // check it work or not. 175 | content.match(/background-size/g).length.should.equal(1); 176 | 177 | cb(); 178 | })) 179 | .on('data', noop) 180 | .on('end', done); 181 | 182 | }); 183 | }); 184 | 185 | -------------------------------------------------------------------------------- /lib/fastimagesize.js: -------------------------------------------------------------------------------- 1 | // 2 | // fast-image-size - Simple stand alone module to just extract the image size from image file without using special image libraries. 3 | // 4 | // Please refer to README.md for this module's documentations. 5 | // 6 | // NOTE: 7 | // - Before changing this code please refer to the 'hacking the code section' on README.md. 8 | // 9 | // Copyright (c) 2013 Ziv Barber; 10 | // 11 | // Permission is hereby granted, free of charge, to any person obtaining 12 | // a copy of this software and associated documentation files (the 13 | // 'Software'), to deal in the Software without restriction, including 14 | // without limitation the rights to use, copy, modify, merge, publish, 15 | // distribute, sublicense, and/or sell copies of the Software, and to 16 | // permit persons to whom the Software is furnished to do so, subject to 17 | // the following conditions: 18 | // 19 | // The above copyright notice and this permission notice shall be 20 | // included in all copies or substantial portions of the Software. 21 | // 22 | // THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 23 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 24 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 25 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 26 | // CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 27 | // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 28 | // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 29 | // 30 | 31 | var fs = require('fs'); 32 | var path = require('path'); 33 | 34 | module.exports = exports = function (file_path, callback) { 35 | 36 | function getJpgSize(buffer_data, retInfo) { 37 | // Skip 5 chars, they are for signature 38 | buffer_data = buffer_data.slice(4); 39 | 40 | var i, next; 41 | while (buffer_data.length) { 42 | // read length of the next block 43 | i = buffer_data.readUInt16BE(0); 44 | 45 | // 0xFFC0 is baseline(SOF) 46 | // 0xFFC2 is progressive(SOF2) 47 | next = buffer_data[i + 1]; 48 | if (next === 0xC0 || next === 0xC2) { 49 | return { 50 | 'height': buffer_data.readUInt16BE(i + 5), 51 | 'width': buffer_data.readUInt16BE(i + 7) 52 | }; 53 | } 54 | 55 | // move to the next block 56 | buffer_data = buffer_data.slice(i + 2); 57 | } 58 | } 59 | 60 | function parseHeaderData(buffer_data, callback_data) { 61 | var retInfo = {}; 62 | 63 | // Detect GIF: 64 | if (buffer_data[0] == 0x47 && buffer_data[1] == 0x49 && buffer_data[2] == 0x46) { 65 | retInfo.type = 'gif'; 66 | retInfo.width = (buffer_data[7] * 256) + buffer_data[6]; 67 | retInfo.height = (buffer_data[9] * 256) + buffer_data[8]; 68 | 69 | // Detect JPEG: 70 | } else if (buffer_data[0] == 0xFF && buffer_data[1] == 0xD8 && buffer_data[2] == 0xFF && (buffer_data[3] == 0xE0 || buffer_data[3] == 0xE1)) { 71 | retInfo.type = 'jpeg'; 72 | var size = getJpgSize(buffer_data, retInfo); 73 | retInfo.width = size.width; 74 | retInfo.height = size.height; 75 | 76 | // Detect PNG: 77 | } else if (buffer_data[0] == 137 && buffer_data[1] == 80 && buffer_data[2] == 78 && buffer_data[3] == 71 && buffer_data[4] == 13 && buffer_data[5] == 10 && buffer_data[6] == 26 && buffer_data[7] == 10) { 78 | retInfo.type = 'png'; 79 | 80 | if (buffer_data[12] == 0x49 && buffer_data[13] == 0x48 && buffer_data[14] == 0x44 && buffer_data[15] == 0x52) { 81 | retInfo.width = (buffer_data[16] * 256 * 256 * 256) + (buffer_data[17] * 256 * 256) + (buffer_data[18] * 256) + buffer_data[19]; 82 | retInfo.height = (buffer_data[20] * 256 * 256 * 256) + (buffer_data[21] * 256 * 256) + (buffer_data[22] * 256) + buffer_data[23]; 83 | } // Endif. 84 | 85 | // Detect BMP: 86 | } else if (buffer_data[0] == 0x42 && buffer_data[1] == 0x4D) { 87 | retInfo.type = 'bmp'; 88 | retInfo.width = (buffer_data[21] * 256 * 256 * 256) + (buffer_data[20] * 256 * 256) + (buffer_data[19] * 256) + buffer_data[18]; 89 | retInfo.height = (buffer_data[25] * 256 * 256 * 256) + (buffer_data[24] * 256 * 256) + (buffer_data[23] * 256) + buffer_data[22]; 90 | } // Endif. 91 | 92 | retInfo.image = file_path; 93 | if (!retInfo.type) { 94 | retInfo.type = 'unknown'; 95 | } // Endif. 96 | 97 | if (callback_data) { 98 | callback_data(retInfo); 99 | } // Endif. 100 | 101 | return retInfo; 102 | }; 103 | 104 | function getSVGImageSize(data) { 105 | var retInfo = {}; 106 | retInfo.width = 0; 107 | retInfo.height = 0; 108 | retInfo.type = 'svg'; 109 | try{ 110 | retInfo.width = data.match(/ -1){ 146 | var fd = fs.openSync(file_path, "r"); 147 | var bufferSize = fs.fstatSync(fd).size; 148 | var buffer = new Buffer(bufferSize); 149 | var bytesRead = fs.readSync(fd, buffer, 0, bufferSize, 0); 150 | fs.closeSync(fd); 151 | return getSVGImageSize(buffer.toString(),null); 152 | }else{ 153 | var fd = fs.openSync(file_path, "r"); 154 | var bufferSize = fs.fstatSync(fd).size; 155 | var buffer = new Buffer(bufferSize); 156 | var bytesRead = fs.readSync(fd, buffer, 0, bufferSize, 0); 157 | fs.closeSync(fd); 158 | return parseHeaderData(buffer, null); 159 | } 160 | 161 | 162 | } // Endif. 163 | }; 164 | 165 | --------------------------------------------------------------------------------