├── .gitignore ├── LICENSE ├── README.md ├── index.js ├── lib ├── custom-gulp-sass.js ├── iconify.js └── output.mustache └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | .idea/ 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 18 | .grunt 19 | 20 | # Compiled binary addons (http://nodejs.org/api/addons.html) 21 | build/Release 22 | 23 | # Dependency directory 24 | # Commenting this out is preferred by some people, see 25 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git- 26 | node_modules 27 | 28 | # Users Environment Variables 29 | .lock-wscript 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Gavro 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | gulp-iconify 2 | ============ 3 | 4 | 'A mystical CSS icon solution', [grunticon](https://github.com/filamentgroup/grunticon)-like build system for [Gulp](https://github.com/gulpjs/gulp): 5 | 6 | '~~grunticon is a Grunt.js~~ gulp-iconify is a gulp task that makes it easy to manage icons and background images for all devices, preferring HD (retina) SVG icons but also provides fallback support for standard definition browsers, and old browsers alike. From a CSS perspective, it's easy to use, as it generates a class referencing each icon, and doesn't use CSS sprites.' 7 | 8 | 9 | ##Usage 10 | ```shell 11 | npm install gulp-iconify --save-dev 12 | ``` 13 | 14 | ###Simple example 15 | ```javascript 16 | gulp.task('default', function() { 17 | iconify({ 18 | src: './img/icons/*.svg' 19 | }); 20 | }); 21 | ``` 22 | 23 | This simple call defaults to the following: 24 | - SVGs will be passed through SVGO (and optimised) 25 | - Rendered PNGs will be saved in: './img/icons/png' 26 | - Rendered SCSS files will NOT be saved 27 | - Rendered CSS files will be saved in: './css' 28 | - If SVG has no width attribute, the default fallback will be 300px 29 | - If SVG has no height attribute, the default fallback will be 200px 30 | - The default styleTemplate fill be used (examples shown below) 31 | - The default styleTemplate will *not* use the height/width slugs 32 | - See [gulp-svg2png](https://github.com/akoenig/gulp-svg2png) for default settings 33 | 34 | ###Customized example 35 | ```javascript 36 | gulp.task('default', function() { 37 | iconify({ 38 | src: './img/icons/*.svg', 39 | pngOutput: './img/icons/png', 40 | scssOutput: './scss', 41 | cssOutput: './css', 42 | styleTemplate: '_icon_gen.scss.mustache', 43 | defaultWidth: '300px', 44 | defaultHeight: '200px', 45 | svgoOptions: { 46 | enabled: true, 47 | options: { 48 | plugins: [ 49 | { removeUnknownsAndDefaults: false }, 50 | { mergePaths: false } 51 | ] 52 | } 53 | }, 54 | svg2pngOptions: { 55 | scaling: 1.0, 56 | verbose: true, 57 | concurrency: null 58 | } 59 | }); 60 | }); 61 | ``` 62 | 63 | Note: To disable SVGO, just set ```svgoOptions: { enabled: ___ }``` to anything but ```true``` . 64 | 65 | ###Example (and default) styleTemplate: 66 | ```mustache 67 | .icon { 68 | background-repeat: no-repeat; 69 | 70 | {{#items}} 71 | &.icon-{{slug}} { 72 | background-image: url('{{{datauri}}}'); 73 | } 74 | 75 | {{/items}} 76 | } 77 | ``` 78 | 79 | ###Example styleTemplate with height/width slugs: 80 | ```mustache 81 | .icon { 82 | background-repeat: no-repeat; 83 | 84 | {{#items}} 85 | &.icon-{{slug}} { 86 | background-image: url('{{{datauri}}}'); 87 | width: {{width}}px; 88 | height: {{height}}px; 89 | } 90 | 91 | {{/items}} 92 | } 93 | ``` -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var iconify = require('./lib/iconify'); 3 | var del = require('del'); 4 | var svg2png = require('gulp-svg2png'); 5 | var sass = require('./lib/custom-gulp-sass'); 6 | var gutil = require('gulp-util'); 7 | var path = require('path'); 8 | var fs = require('fs'); 9 | 10 | function getErrors(opts) { 11 | var error = {}; 12 | 13 | if(!opts.src) { 14 | error.src = "Error: src not defined; please specify your source (src: './img/icons/*.svg')."; 15 | } 16 | 17 | if(Object.keys(error).length) { 18 | Object.keys(error).forEach(function(k) { 19 | gutil.log(error[k]); 20 | }); 21 | 22 | process.exit(); 23 | } 24 | } 25 | 26 | function setFallbacks(opts) { 27 | var warning = {}; 28 | 29 | if(!opts.pngOutput) { 30 | opts.pngOutput = path.dirname(opts.src)+'/png'; 31 | warning.pngOutput = "Info: No pngOutput folder defined. Using fallback ("+opts.pngOutput+")."; 32 | } 33 | 34 | if(opts.cssOutput === undefined) { 35 | opts.cssOutput = './css'; 36 | warning.cssOutput = "Info: No cssOutput folder defined. Using fallback ("+opts.cssOutput+")."; 37 | } else if (opts.cssOutput === false) { 38 | opts.cssDisabled = true; 39 | warning.cssOutput = "Info: CSS generation has been disabled. CSS files will not be saved."; 40 | } 41 | 42 | if(!opts.scssOutput) { 43 | opts.scssOutput = './scss'; 44 | opts.scssDisabled = true; 45 | 46 | // check if "./scss" exists, if not: remember to remove the folder lateron. 47 | fs.stat(path.normalize(opts.scssOutput), function (err) { 48 | if (err) { 49 | // File doesn't exist - remove scss folder on finish. 50 | opts.scssRemoveDir = true; 51 | } 52 | }); 53 | 54 | warning.scssOutput = "Info: No scssOutput folder defined. SCSS files will not be saved (temporary files will be saved to '/scss')."; 55 | } 56 | 57 | if(!opts.styleTemplate) { 58 | opts.styleTemplate = path.join(__dirname, 'lib/output.mustache'); 59 | warning.styleTemplate = "Info: No styleTemplate defined. Using default template."; 60 | } 61 | 62 | if(!opts.svgoOptions) { 63 | opts.svgoOptions = { enabled: true }; 64 | warning.svgoOptions = "Info: No SVGO options defined, enabling SVGO by default."; 65 | } 66 | 67 | if(!opts.svg2pngOptions) { 68 | opts.svg2pngOptions = { 69 | options: {}, 70 | verbose: false, 71 | concurrency: 8 72 | }; 73 | warning.svg2pngOptions = "Info: No svg2png options defined. Using default settings."; 74 | } 75 | 76 | if(!opts.defaultWidth) { 77 | opts.defaultWidth = "300px"; 78 | warning.defaultWidth = "Info: No defaultWidth defined. Using fallback ("+opts.defaultWidth+") if SVG has no width."; 79 | } 80 | 81 | if(!opts.defaultHeight) { 82 | opts.defaultHeight = "200px"; 83 | warning.defaultHeight = "Info: No defaultHeight defined. Using fallback ("+opts.defaultHeight+") if SVG has no height."; 84 | } 85 | 86 | if(Object.keys(warning).length) { 87 | Object.keys(warning).forEach(function(k) { 88 | gutil.log(warning[k]); 89 | }); 90 | } 91 | } 92 | 93 | module.exports = function(opts) { 94 | opts = opts || {}; 95 | opts.scssDisabled = false; 96 | 97 | getErrors(opts); 98 | setFallbacks(opts); 99 | 100 | gulp.task('iconify-clean', function(cb) { 101 | del([opts.scssOutput+'/*icons.*.scss', opts.cssOutput+'/*icons.*.css', opts.pngOutput+'/*.png'], cb); 102 | }); 103 | 104 | gulp.task('iconify-convert', ['iconify-clean'], function() { 105 | gulp.src(opts.src) 106 | .pipe(iconify({ 107 | styleTemplate: opts.styleTemplate, 108 | styleName: '_icons.svg.scss', 109 | svgoOptions: opts.svgoOptions, 110 | defaultWidth: opts.defaultWidth, 111 | defaultHeight: opts.defaultHeight 112 | })) 113 | .pipe(gulp.dest(opts.scssOutput)); 114 | 115 | var stream = gulp.src(opts.src) 116 | .pipe(svg2png(opts.svg2pngOptions.options, opts.svg2pngOptions.verbose, opts.svg2pngOptions.concurrency)) 117 | .pipe(gulp.dest(opts.pngOutput)) 118 | .pipe(iconify({ 119 | styleTemplate: opts.styleTemplate, 120 | styleName: '_icons.png.scss', 121 | defaultWidth: opts.defaultWidth, 122 | defaultHeight: opts.defaultHeight 123 | })) 124 | .pipe(gulp.dest(opts.scssOutput)); 125 | 126 | return stream; 127 | }); 128 | 129 | gulp.task('iconify-fallback', ['iconify-clean', 'iconify-convert'], function() { 130 | var stream = gulp.src(opts.pngOutput+'/*.png') 131 | .pipe(iconify({ 132 | styleTemplate: opts.styleTemplate, 133 | styleName: '_icons.fallback.scss', 134 | noConvert: true, 135 | cssOutputTarget: opts.cssOutput, 136 | pngOutputTarget: opts.pngOutput 137 | })) 138 | .pipe(gulp.dest(opts.scssOutput)); 139 | 140 | return stream; 141 | }); 142 | 143 | gulp.task('iconify-sass', ['iconify-convert', 'iconify-fallback'], function() { 144 | if (opts.cssDisabled) { 145 | return false; 146 | } 147 | var stream = gulp.src(opts.scssOutput+'/_icons.*.scss') 148 | .pipe(sass({ 149 | outputStyle: 'compressed' 150 | })) 151 | .pipe(gulp.dest(opts.cssOutput)); 152 | 153 | return stream; 154 | }); 155 | 156 | gulp.task('iconify', ['iconify-convert', 'iconify-fallback', 'iconify-sass'], function() { 157 | // remove SCSS folder/files if SCSS output is disabled 158 | if(opts.scssDisabled) { 159 | if(opts.scssRemoveDir) { 160 | del.sync([opts.scssOutput]); 161 | } else { 162 | del.sync([opts.scssOutput+'/_icons.*.scss']); 163 | } 164 | } 165 | }); 166 | 167 | gulp.start('iconify'); 168 | }; 169 | -------------------------------------------------------------------------------- /lib/custom-gulp-sass.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var gutil = require('gulp-util'); 4 | var through = require('through2'); 5 | var assign = require('object-assign'); 6 | var path = require('path'); 7 | var applySourceMap = require('vinyl-sourcemaps-apply'); 8 | 9 | var PLUGIN_NAME = 'gulp-sass'; 10 | 11 | ////////////////////////////// 12 | // Main Gulp Sass function 13 | ////////////////////////////// 14 | var gulpSass = function gulpSass(options, sync) { 15 | return through.obj(function(file, enc, cb) { 16 | var opts, 17 | filePush, 18 | errorM, 19 | callback, 20 | result; 21 | 22 | if (file.isNull()) { 23 | return cb(null, file); 24 | } 25 | if (file.isStream()) { 26 | return cb(new gutil.PluginError(PLUGIN_NAME, 'Streaming not supported')); 27 | } 28 | if (!file.contents.length) { 29 | file.path = gutil.replaceExtension(file.path, '.css'); 30 | return cb(null, file); 31 | } 32 | 33 | opts = assign({}, options); 34 | opts.data = file.contents.toString(); 35 | 36 | // Ensure `indentedSyntax` is true if a `.sass` file 37 | if (path.extname(file.path) === '.sass') { 38 | opts.indentedSyntax = true; 39 | } 40 | 41 | // Ensure file's parent directory in the include path 42 | if (opts.includePaths) { 43 | if (typeof opts.includePaths === 'string') { 44 | opts.includePaths = [opts.includePaths]; 45 | } 46 | } 47 | else { 48 | opts.includePaths = []; 49 | } 50 | 51 | opts.includePaths.unshift(path.dirname(file.path)); 52 | 53 | // Generate Source Maps if plugin source-map present 54 | if (file.sourceMap) { 55 | opts.sourceMap = file.path; 56 | opts.omitSourceMapUrl = true; 57 | opts.sourceMapContents = true; 58 | } 59 | 60 | ////////////////////////////// 61 | // Handles returning the file to the stream 62 | ////////////////////////////// 63 | filePush = function filePush(sassObj) { 64 | var sassMap, 65 | sassMapFile, 66 | sassFileSrc, 67 | sassFileSrcPath, 68 | sourceFileIndex, 69 | filteredSources; 70 | 71 | // Build Source Maps! 72 | if (sassObj.map) { 73 | // Transform map into JSON 74 | sassMap = JSON.parse(sassObj.map.toString()); 75 | // Grab the stdout and transform it into stdin 76 | sassMapFile = sassMap.file.replace('stdout', 'stdin'); 77 | // Grab the base file name that's being worked on 78 | sassFileSrc = file.relative; 79 | // Grab the path portion of the file that's being worked on 80 | sassFileSrcPath = path.dirname(sassFileSrc); 81 | if (sassFileSrcPath) { 82 | //Prepend the path to all files in the sources array except the file that's being worked on 83 | for (sourceFileIndex = 0; sourceFileIndex < sassMap.sources.length; sourceFileIndex++) { 84 | if (sourceFileIndex !== sassMap.sources.indexOf(sassMapFile)) { 85 | sassMap.sources[sourceFileIndex] = path.join(sassFileSrcPath, sassMap.sources[sourceFileIndex]); 86 | } 87 | } 88 | } 89 | // Remove 'stdin' from souces and replace with filenames! 90 | filteredSources = sassMap.sources.filter(function(src) { 91 | if (src.indexOf('stdin') === -1) { 92 | return src; 93 | } 94 | }); 95 | sassMap.sources = filteredSources; 96 | sassMap.sources.unshift(sassFileSrc); 97 | // Replace the map file with the original file name (but new extension) 98 | sassMap.file = gutil.replaceExtension(sassFileSrc, '.css'); 99 | // Apply the map 100 | applySourceMap(file, sassMap); 101 | } 102 | 103 | file.contents = sassObj.css; 104 | file.path = gutil.replaceExtension(file.path, '.css'); 105 | 106 | var parts = file.path.split('/'); 107 | if(parts.length) { 108 | parts[parts.length - 1] = parts[parts.length - 1].replace(/^_+/, ''); 109 | file.path = parts.join('/'); 110 | } 111 | 112 | cb(null, file); 113 | }; 114 | 115 | ////////////////////////////// 116 | // Handles error message 117 | ////////////////////////////// 118 | errorM = function errorM(error) { 119 | var relativePath = '', 120 | filePath = error.file === 'stdin' ? file.path : error.file, 121 | message = ''; 122 | 123 | filePath = filePath ? filePath : file.path; 124 | relativePath = path.relative(process.cwd(), filePath); 125 | 126 | message += gutil.colors.underline(relativePath) + '\n'; 127 | message += error.formatted; 128 | 129 | error.messageFormatted = message; 130 | error.message = gutil.colors.stripColor(message); 131 | 132 | return cb(new gutil.PluginError( 133 | PLUGIN_NAME, error 134 | )); 135 | }; 136 | 137 | if (sync !== true) { 138 | ////////////////////////////// 139 | // Async Sass render 140 | ////////////////////////////// 141 | callback = function(error, obj) { 142 | if (error) { 143 | return errorM(error); 144 | } 145 | filePush(obj); 146 | }; 147 | 148 | gulpSass.compiler.render(opts, callback); 149 | } 150 | else { 151 | ////////////////////////////// 152 | // Sync Sass render 153 | ////////////////////////////// 154 | try { 155 | result = gulpSass.compiler.renderSync(opts); 156 | 157 | filePush(result); 158 | } 159 | catch (error) { 160 | return errorM(error); 161 | } 162 | } 163 | }); 164 | }; 165 | 166 | ////////////////////////////// 167 | // Sync Sass render 168 | ////////////////////////////// 169 | gulpSass.sync = function sync(options) { 170 | return gulpSass(options, true); 171 | }; 172 | 173 | ////////////////////////////// 174 | // Log errors nicely 175 | ////////////////////////////// 176 | gulpSass.logError = function logError(error) { 177 | var message = new gutil.PluginError('sass', error.messageFormatted).toString(); 178 | process.stderr.write(message + '\n'); 179 | this.emit('end'); 180 | }; 181 | 182 | ////////////////////////////// 183 | // Store compiler in a prop 184 | ////////////////////////////// 185 | gulpSass.compiler = require('node-sass'); 186 | 187 | module.exports = gulpSass; -------------------------------------------------------------------------------- /lib/iconify.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | var path = require('path'); 3 | var mustache = require('mustache'); 4 | var through = require('through'); 5 | var gutil = require('gulp-util'); 6 | var imacss = require('imacss'); 7 | var svgo = require('svgo'); 8 | var dom = require('xmldom'); 9 | 10 | var getDimensions = function (type, image) { 11 | var dimensions = {}; 12 | 13 | if(type === 'svg') { 14 | var doc = new dom.DOMParser().parseFromString(image.contents.toString('utf-8')); 15 | dimensions.width = Math.round(doc.documentElement.getAttribute('width')); 16 | dimensions.height = Math.round(doc.documentElement.getAttribute('height')); 17 | } else { 18 | var hexString = image.contents.toString("hex"); 19 | var i = 16, l; 20 | 21 | for( l = hexString.length; i < l; i++ ){ 22 | var d = hexString.slice(i, i+8); 23 | if( d === "49484452" ){ 24 | i = i+8; 25 | break; 26 | } 27 | } 28 | 29 | dimensions.width = parseInt(hexString.slice(i, i+8).toString(16), 16); 30 | i = i+8; 31 | dimensions.height = parseInt(hexString.slice(i, i+8).toString(16), 16); 32 | } 33 | 34 | return dimensions; 35 | } 36 | 37 | imacss.partialtransform = function partialtransform (glob, css, opts) { 38 | // imacss extra includes 39 | var domain = require('domain'); 40 | var through = require('through2'); 41 | var pipeline = require('imacss/lib'); 42 | var pkg = require('../package.json'); 43 | 44 | var execution = domain.create(); 45 | var transformation; 46 | 47 | css = css || pkg.name; 48 | 49 | execution.on('error', function (err) { 50 | transformation.emit('error', err); 51 | }); 52 | 53 | execution.run(function () { 54 | function normalizeSVGs (image, enc, callback) { 55 | var dim; 56 | 57 | if(image.mime === 'image/svg+xml') { 58 | if(typeof opts.svgoOptions !== 'undefined' && (typeof opts.svgoOptions.enabled === 'undefined' || opts.svgoOptions.enabled === true)) { 59 | if(typeof opts.svgoOptions.options === 'object') { 60 | var svg = new svgo(opts.svgoOptions.options); 61 | } else { 62 | var svg = new svgo(); 63 | } 64 | 65 | svg.optimize(String(image.contents), function(result) { 66 | if (result.error) { 67 | gutil.log('Error: ' + result.error + ' [file: '+image.name+']'); 68 | } else { 69 | image.contents = new Buffer(result.data); 70 | } 71 | }); 72 | } 73 | 74 | image.datauri = 'data:'+image.mime+';charset=UTF-8,'+encodeURIComponent(image.contents.toString('utf-8')); 75 | dim = getDimensions('svg', image); 76 | } else { 77 | dim = getDimensions('png', image); 78 | } 79 | 80 | image.width = String(dim.width || opts.defaultWidth).replace(/px/, ""); 81 | image.height = String(dim.height || opts.defaultHeight).replace(/px/, ""); 82 | 83 | this.push(image); 84 | callback(); 85 | } 86 | 87 | transformation = pipeline.createFileStream(glob) 88 | .pipe(pipeline.purify()) 89 | .pipe(pipeline.slugify()) 90 | .pipe(pipeline.mimeify()) 91 | .pipe(pipeline.urify()) 92 | .pipe(through.obj(normalizeSVGs)); 93 | }); 94 | 95 | return transformation; 96 | }; 97 | 98 | module.exports = function(opts) { 99 | var tpl = fs.readFileSync(opts.styleTemplate).toString(); 100 | 101 | var buffer = []; 102 | var noConvert = opts.noConvert; 103 | 104 | var bufferContents = function(file) { 105 | if(noConvert) { 106 | buffer.push({ 107 | slug: file.relative.replace('.'+getExtension(file.relative), ''), 108 | datauri: path.join(path.relative(opts.cssOutputTarget, opts.pngOutputTarget), file.relative) 109 | }); 110 | } else { 111 | imacss 112 | .partialtransform(file, 'icon', opts) 113 | .on('data', function (selector) { 114 | buffer.push(selector); 115 | }) 116 | .once('error', this.emit.bind(this, 'error')); 117 | } 118 | }; 119 | 120 | var endStream = function() { 121 | this.emit('data', new gutil.File({ 122 | contents: new Buffer(mustache.render(tpl, { 123 | items: buffer 124 | }), 'utf8'), 125 | path: opts.styleName 126 | })); 127 | this.emit('end'); 128 | }; 129 | 130 | var getExtension = function (filename) { 131 | var ext = path.extname(filename||'').split('.'); 132 | return ext[ext.length - 1]; 133 | }; 134 | 135 | return new through(bufferContents, endStream); 136 | }; 137 | -------------------------------------------------------------------------------- /lib/output.mustache: -------------------------------------------------------------------------------- 1 | .icon { 2 | background-repeat: no-repeat; 3 | 4 | {{#items}} 5 | &.icon-{{slug}} { 6 | background-image: url('{{{datauri}}}'); 7 | } 8 | 9 | {{/items}} 10 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gulp-iconify", 3 | "version": "0.6.0", 4 | "description": "'A mystical CSS icon solution', grunticon-like build system.", 5 | "author": "Gabrijel Gavranović (http://gavro.nl)", 6 | "keywords": [ 7 | "gulpplugin", 8 | "base64", 9 | "svg", 10 | "css", 11 | "image", 12 | "grunticon" 13 | ], 14 | "repository": { 15 | "type": "git", 16 | "url": "git@github.com:gavro/gulp-iconify.git" 17 | }, 18 | "main": "index.js", 19 | "dependencies": { 20 | "path": "*", 21 | "domain": "*", 22 | "gulp": ">3.0", 23 | "gulp-util": "^3.0", 24 | "mustache": "~2.0.0", 25 | "through": "~2.3.6", 26 | "through2": "^2.0.0", 27 | "imacss": "~0.3.0", 28 | "gulp-svg2png": "~2.0.0", 29 | "gulp-sass": "~2.1.0", 30 | "svgo": "~0.5.0", 31 | "del": "~1.1.1", 32 | "xmldom": "~0.1.19", 33 | "node-sass": "^3.4.1", 34 | "object-assign": "^4.0.1", 35 | "vinyl-sourcemaps-apply": "^0.2.0" 36 | }, 37 | "bugs": { 38 | "url": "https://github.com/gavro/gulp-iconify/issues" 39 | }, 40 | "homepage": "https://github.com/gavro/gulp-iconify", 41 | "scripts": { 42 | "test": "echo \"Error: no test specified\" && exit 1" 43 | }, 44 | "license": "MIT" 45 | } 46 | --------------------------------------------------------------------------------