├── .nvmrc ├── .gitignore ├── assets └── svgo-compressor.png ├── webpack.skpm.config.js ├── src ├── manifest.json └── plugin.js ├── package.json ├── LICENSE.md ├── .appcast.xml └── README.md /.nvmrc: -------------------------------------------------------------------------------- 1 | 14.17.0 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | SVGO Compressor.sketchplugin 4 | src/svgo-plugins.js 5 | -------------------------------------------------------------------------------- /assets/svgo-compressor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sketch-hq/svgo-compressor/HEAD/assets/svgo-compressor.png -------------------------------------------------------------------------------- /webpack.skpm.config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Function that mutates original webpack config. 3 | * Supports asynchronous changes when promise is returned. 4 | * 5 | * @param {object} config - original webpack config. 6 | * @param {boolean} isPluginCommand - wether the config is for a plugin command or a resource 7 | **/ 8 | module.exports = function (config, isPluginCommand) { 9 | if (!config.resolve) { 10 | config.resolve = {} 11 | } 12 | if (!config.resolve.alias) { 13 | config.resolve.alias = {} 14 | } 15 | config.resolve.alias.fs = '@skpm/fs' 16 | } 17 | -------------------------------------------------------------------------------- /src/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "compatibleVersion": 3.8, 3 | "bundleVersion": 1, 4 | "identifier": "com.sketchapp.plugins.svgo-compressor", 5 | "icon": "svgo-compressor.png", 6 | "commands": [ 7 | { 8 | "name": "About SVGO Compressor", 9 | "identifier": "svgCompress", 10 | "script": "./plugin.js", 11 | "scope": "application", 12 | "handlers": { 13 | "run" : "showAbout", 14 | "actions": { 15 | "ExportSlices": "compress", 16 | "Export": "compress" 17 | } 18 | } 19 | } 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "svgo-compressor", 3 | "version": "2.1.2", 4 | "license": "MIT", 5 | "description": "A Plugin that compresses SVG assets using SVGO, right when you export them. This Plugin *requires* Sketch 3.8.", 6 | "skpm": { 7 | "name": "SVGO Compressor", 8 | "main": "SVGO Compressor.sketchplugin", 9 | "manifest": "src/manifest.json", 10 | "assets": [ 11 | "assets/**/*" 12 | ] 13 | }, 14 | "main": "src/plugin.js", 15 | "scripts": { 16 | "build": "skpm-build", 17 | "watch": "skpm-build --watch", 18 | "start": "skpm-build --watch --run", 19 | "postinstall": "npm run build && skpm-link", 20 | "test": "echo \"Error: no test specified\" && exit 1", 21 | "publish": "skpm publish --appcast=.appcast.xml" 22 | }, 23 | "author": "Sketch", 24 | "devDependencies": { 25 | "@skpm/builder": "0.8.0", 26 | "serialize-javascript": "6.0.0" 27 | }, 28 | "repository": "https://github.com/sketch-hq/svgo-compressor", 29 | "dependencies": { 30 | "@skpm/dialog": "0.4.2", 31 | "@skpm/fs": "0.2.6", 32 | "svgo": "2.8.0" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016-2020 Sketch BV 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 | -------------------------------------------------------------------------------- /.appcast.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /src/plugin.js: -------------------------------------------------------------------------------- 1 | import fs from '@skpm/fs' 2 | import dialog from '@skpm/dialog' 3 | import UI from 'sketch/ui' 4 | import { optimize } from 'svgo' 5 | 6 | export function showAbout() { 7 | // Plugin was run from the menu, so let's open the about window 8 | const response = dialog.showMessageBox({ 9 | message: "About SVGO Compressor", 10 | detail: "This Plugin uses SVGO to compress SVG assets exported from Sketch.\n\nIt works automatically whenever you export to SVG, so you don’t need to do anything special. Just work on your design as always, and enjoy smaller & cleaner SVG files." 11 | }) 12 | } 13 | 14 | export function compress(context) { 15 | const exports = context.actionContext.exports 16 | let filesToCompress = 0 17 | exports.forEach(currentExport => { 18 | if (currentExport.request.format() == 'svg') { 19 | filesToCompress++ 20 | let currentFile 21 | // This was broken momentarily between Sketch 76 and 77 22 | // so we need to use a workaround 23 | currentFile = currentExport.path 24 | if (currentExport.path.path !== undefined) { 25 | currentFile = currentExport.path.path() 26 | } 27 | const svgString = fs.readFileSync(currentFile, 'utf8') 28 | 29 | // Load external SVGO config, if it exists 30 | const homeDir = NSHomeDirectory() 31 | const configFile = homeDir + '/Library/Application\ Support/com.bohemiancoding.sketch3/Plugins/svgo.config.js' 32 | let externalConfig = {} 33 | if (fs.existsSync(configFile)) { 34 | // Do not explode if the file is not valid JS 35 | try { 36 | externalConfig = eval(fs.readFileSync(configFile, 'utf8')) 37 | } catch (error) { 38 | console.log("Error: " + error.message) 39 | } 40 | } 41 | const defaultConfig = { 42 | path: currentFile, 43 | multipass: true, 44 | plugins: [ 45 | { 46 | name: 'preset-default', 47 | params: { 48 | overrides: { 49 | inlineStyles: false, 50 | removeViewBox: false, 51 | cleanupEnableBackground: false, 52 | removeHiddenElems: false, 53 | convertShapeToPath: false, 54 | moveElemsAttrsToGroup: false, 55 | moveGroupAttrsToElems: false, 56 | convertPathData: false, 57 | } 58 | } 59 | }, 60 | 'convertStyleToAttrs', 61 | 'cleanupListOfValues', 62 | 'sortAttrs' 63 | ], 64 | } 65 | const config = { ...defaultConfig, ...externalConfig } 66 | const result = optimize(svgString, config) 67 | fs.writeFileSync(currentFile, result.data, 'utf8') 68 | } 69 | }) 70 | if (filesToCompress > 0) { 71 | UI.message(`SVGO Compressor: ${filesToCompress} file${filesToCompress == 1 ? '' : 's'} compressed`) 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SVGO Compressor 2 | 3 | A Plugin that compresses SVG assets using SVGO, right when you export them. 4 | 5 | ## Install 6 | 7 | - Download [SVGO Compressor](https://github.com/sketch-hq/svgo-compressor/releases/latest/download/SVGO.Compressor.sketchplugin.zip) & unzip it. 8 | - Double click **SVGO Compressor.sketchplugin** to install the Plugin. 9 | 10 | ## Usage 11 | 12 | Selecting menu items or hitting keys is out of fashion. SVGO Compressor will compress your SVG assets whenever you export them, without you having to do anything. 13 | 14 | You’ll get a message on your document window to let you know the compression worked as expected. 15 | 16 | If you need uncompressed SVG assets, you can temporarily disable the Plugin by opening Sketch’s **Preferences › Plugins** and unchecking 'SVGO Compressor'. Or you can right-click any layer and select **Copy SVG Code**, and that will give you the original, uncompressed code. 17 | 18 | ## Custom SVGO configuration 19 | 20 | SVGO Compressor uses a default configuration that does a reasonable job of compressing SVG code, while maintaining compatibility and avoiding rendering issues. If you need to change the defaults, you can do so by creating an `svgo.config.js` file in Sketch's `Plugins` directory (located by default in `~/Library/Application\ Support/com.bohemiancoding.sketch3/Plugins/`). 21 | 22 | For a complete reference of what your SVGO config should look like, see [SVGO’s configuration documentation](https://github.com/svg/svgo#configuration). 23 | 24 | Any option that is not set on your custom configuration will use the defaults set by SVGO Compressor. For example, here's how a sample configuration to output unminified code could look like: 25 | 26 | ```javascript 27 | module.exports = { 28 | js2svg: { 29 | indent: 2, 30 | pretty: true, 31 | } 32 | } 33 | ``` 34 | 35 | Keep in mind that our defaults do not match the ones in SVGO 100%. If you use the `preset-default` option in SVGO your results may vary from the ones this plugin exports. For the record, here's the default configuration we use: 36 | 37 | ``` 38 | { 39 | path: currentFile, // This is the path to the currently exported SVG asset 40 | multipass: true, 41 | plugins: [ 42 | { 43 | name: 'preset-default', 44 | params: { 45 | overrides: { 46 | inlineStyles: false, 47 | convertStyleToAttrs: true, 48 | cleanupListOfValues: true, 49 | removeViewBox: false, 50 | cleanupEnableBackground: false, 51 | removeHiddenElems: false, 52 | convertShapeToPath: false, 53 | moveElemsAttrsToGroup: false, 54 | moveGroupAttrsToElems: false, 55 | convertPathData: false, 56 | sortAttrs: true, 57 | } 58 | } 59 | } 60 | ], 61 | } 62 | ``` 63 | 64 | 65 | Again, for more information about custom configurations please refer to SVGO's own documentation. Please note that the custom plugins feature is untested in SVGO Compressor, so it may or may not work. 66 | 67 | ## Acknowledgements 68 | 69 | We would like to thank: 70 | 71 | - The [SVGO project](https://github.com/svg/svgo), for creating the golden standard for SVG compression. 72 | - [Andrey Shakhmin](https://github.com/turbobabr), for his inspiration during the [Hamburg Hackathon](http://designtoolshackday.com), where he showed us how to use node modules inside Sketch. 73 | 74 | ## Development 75 | 76 | This plugin is built using [skpm](https://github.com/skpm/skpm). To build it, just run 77 | 78 | ```bash 79 | npm i 80 | npm run build 81 | ``` 82 | 83 | To edit the Plugin's code, edit the code in `src` and run `npm run build`. You can also run `npm run watch` to automatically rebuild the Plugin every time you make changes in the code. 84 | --------------------------------------------------------------------------------