├── .appcast.xml ├── .gitignore ├── LICENSE ├── README.md ├── artwork ├── Icon.sketch ├── text-to-json.gif └── text-to-styles.gif ├── assets ├── icon-64.png └── icon.png ├── package-lock.json ├── package.json ├── sample ├── text-styles-sample-simple.sketch ├── text-styles-sample.sketch ├── tokens-array.json └── tokens-object.json ├── src ├── createTextStyles.js ├── generators.js ├── manifest.json ├── maninfest.all.json ├── renderJSON.js ├── renderStyleSheet.js └── setupDocument.js └── text-to-styles.sketchplugin └── Contents ├── Resources ├── icon-64.png └── icon.png └── Sketch ├── __createTextStyles.js ├── __createTextStyles.js.map ├── __renderJSON.js ├── __renderJSON.js.map ├── __renderStyleSheet.js ├── __renderStyleSheet.js.map ├── __setupDocument.js ├── __setupDocument.js.map └── manifest.json /.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 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # build artifacts 2 | plugin.sketchplugin 3 | 4 | # npm 5 | node_modules 6 | .npm 7 | npm-debug.log 8 | 9 | # mac 10 | .DS_Store 11 | 12 | # WebStorm 13 | .idea 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Thomas Brasington 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. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![logo](https://github.com/tbrasington/text-to-styles/raw/master/assets/icon.png) 2 | 3 | # Text to Styles 4 | 5 | ![demo of exporting text layers to text styles](https://github.com/tbrasington/text-to-styles/raw/master/artwork/text-to-styles.gif) 6 | 7 | 8 | ## What? 9 | 10 | Generate all the Sketch text styles you have based on a config set up in your artboards. No more making and maintaining multiple variations! 11 | 12 | ## Why? 13 | 14 | If your text style system requires the use of different alignments or colour variations you are faced with either detaching styles, risking updates overwrite the tweaks you make or you manually have to create every style. 15 | 16 | This plugin aims to solve the generation and maintenance of Sketch text styles where you need colour and alignment variations of your text styles. 17 | 18 | ## Installation 19 | 20 | [![Install Text to Styles with Sketchpacks](http://sketchpacks-com.s3.amazonaws.com/assets/badges/sketchpacks-badge-install.png "Install Text to Styles with Sketchpacks")](https://sketchpacks.com/tbrasington/text-to-styles/install) 21 | 22 | Either install via Sketchpacks, Sketch Runner or... 23 | 24 | 1. Download from the [releases page](https://github.com/tbrasington/text-to-styles/releases) 25 | 2. Unzip 26 | 3. Double Click on `text-to-styles.sketchplugin` 27 | 28 | ## Text Style naming convention 29 | This is an opinioated plugin on how Text Styles should be named. The naming convention is: 30 | 31 | `Style Name/Colour/Alignment/Breakpoint Varation` 32 | 33 | The breakpoint variation is optional. 34 | 35 | 36 | ## Your Page setup 37 | 38 | There is a sample project under [sample/text-styles-sample.sketch](https://github.com/tbrasington/text-to-styles/blob/master/sample/text-styles-sample.sketch) to base your text and colour tokens off 39 | 40 | You will need to set your Page structure and naming like this: 41 | 42 | - Styles 43 | - Colours or Colors 44 | - Alignments 45 | 46 | Please note that this is case sensitive! 47 | 48 | ### Styles 49 | Add your base text styles here. This plugin will pick up on: 50 | 51 | - Font Family 52 | - Font Size 53 | - Font Weight 54 | - Font Style 55 | - Line Height 56 | - Letter Spacing 57 | - Text Transform (uppercase,lowercase) 58 | - Paragraph Spacing 59 | 60 | #### Breakpoint adjustments 61 | If you wish to add breakpoint adjustment for styles use the following naming syntax for the layer 62 | 63 | `Style Name/Breakpoint Variation` 64 | 65 | e.g. 66 | 67 | `Heading 1/BP1` 68 | 69 | The plugin will then split the name and add the breakpoint to the end of the text style name 70 | 71 | ### Colours 72 | Add shapes here with the colour value you want to render. The name of the layer defines the name in the Text Style palette. 73 | Order is done alphabetically. 74 | 75 | ### Alignments 76 | Add your alignments here by setting each text layer to your preffered alignment. 77 | Order is set Left, Center, Right. 78 | 79 | ## Commands 80 | 81 | There are several commands available `Run Document Setup`, `Create Text Styles`, `Render Style Sheet` and `Export JSON Tokens` 82 | 83 | ### Run Document Setup 84 | Sets up the Sketch document Page names with sample Styles, Alignments and Colors. You can then remove or modify them as you see fit. 85 | 86 | ### Create Text Styles 87 | Converts the contents of Styles, Alignments and Colors into text styles. 88 | 89 | ### Render Style Sheet 90 | This will reate a Page called "Rendered Styles" and create every combination within your text styles. Please make sure you have run the Create Text Styles command before. 91 | 92 | #### Performance 93 | This command can take a long time to run. The more combinations the longer it will take. 94 | 95 | ### Export JSON Tokens 96 | 97 | ![demo of exporting text layers to json](https://github.com/tbrasington/text-to-styles/raw/master/artwork/text-to-json.gif) 98 | 99 | 100 | This will export out the text styles and colour palettes as a JSON file to use in other applications. Perhaps your production code base or documentation. 101 | 102 | You will be presented with two options for formatting the text style json. Either an Array or an Object. Depending on your needs you may want the Object. 103 | 104 | 105 | #### Array formatting 106 | 107 | ``` 108 | { 109 | "name": "Style 1", 110 | "styles": { 111 | "fontFamily": "SFProDisplay-Semibold", 112 | "fontSize": "28px", 113 | "lineHeight": "34px", 114 | "fontWeight": 8, 115 | "paragraphSpacing": 25, 116 | "letterSpacing": "0.02em", 117 | "textTransform": "none" 118 | }, 119 | "alignments": [ 120 | "left", 121 | "right", 122 | "center" 123 | ], 124 | "adjustments": [ 125 | { 126 | "name": "BP3", 127 | "styles": { 128 | "fontSize": "42px", 129 | "lineHeight": "48px", 130 | "paragraphSpacing": 100, 131 | "letterSpacing": "0.03em" 132 | } 133 | } 134 | ] 135 | } 136 | ``` 137 | #### Object formatting 138 | 139 | ``` 140 | "Style 1": { 141 | "name": "Style 1", 142 | "styles": { 143 | "fontFamily": "SFProDisplay-Semibold", 144 | "fontSize": "28px", 145 | "lineHeight": "34px", 146 | "fontWeight": 8, 147 | "paragraphSpacing": 25, 148 | "letterSpacing": "0.02em", 149 | "textTransform": "none" 150 | }, 151 | "alignments": [ 152 | "left", 153 | "right", 154 | "center" 155 | ], 156 | "adjustments": [ 157 | { 158 | "name": "BP3", 159 | "styles": { 160 | "fontSize": "42px", 161 | "lineHeight": "48px", 162 | "paragraphSpacing": 100, 163 | "letterSpacing": "0.03em" 164 | } 165 | } 166 | ] 167 | } 168 | ``` 169 | 170 | 171 | 172 | --- 173 | 174 | @tbrasington | tbrasington.com 175 | -------------------------------------------------------------------------------- /artwork/Icon.sketch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tbrasington/text-to-styles/d3e980b2f06330ddb62690819699e97239a26269/artwork/Icon.sketch -------------------------------------------------------------------------------- /artwork/text-to-json.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tbrasington/text-to-styles/d3e980b2f06330ddb62690819699e97239a26269/artwork/text-to-json.gif -------------------------------------------------------------------------------- /artwork/text-to-styles.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tbrasington/text-to-styles/d3e980b2f06330ddb62690819699e97239a26269/artwork/text-to-styles.gif -------------------------------------------------------------------------------- /assets/icon-64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tbrasington/text-to-styles/d3e980b2f06330ddb62690819699e97239a26269/assets/icon-64.png -------------------------------------------------------------------------------- /assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tbrasington/text-to-styles/d3e980b2f06330ddb62690819699e97239a26269/assets/icon.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "text-to-styles", 3 | "version": "4.0.0", 4 | "license": "MIT", 5 | "repository": "https://github.com/tbrasington/text-to-styles.git", 6 | "description": "Generates Sketch Text Styles from Layers to easily manage, size, colour and alignment variations", 7 | "engines": { 8 | "sketch": ">=53.0" 9 | }, 10 | "skpm": { 11 | "name": "Text to Styles", 12 | "manifest": "src/manifest.json", 13 | "main": "text-to-styles.sketchplugin", 14 | "assets": [ 15 | "assets/**/*" 16 | ], 17 | "identifier": "text-to-styles.plugin" 18 | }, 19 | "scripts": { 20 | "build": "skpm-build", 21 | "watch": "skpm-build --watch", 22 | "start": "skpm-build --watch --run", 23 | "postinstall": "npm run build && skpm-link" 24 | }, 25 | "devDependencies": { 26 | "@skpm/builder": "^0.7.5" 27 | }, 28 | "author": "Thomas Brasington ", 29 | "dependencies": { 30 | "@skpm/dialog": "^0.4.0", 31 | "@skpm/fs": "^0.1.4", 32 | "@skpm/path": "^0.1.4", 33 | "json-format": "^1.0.1", 34 | "path": "^0.12.7", 35 | "ramda": "^0.26.1" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /sample/text-styles-sample-simple.sketch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tbrasington/text-to-styles/d3e980b2f06330ddb62690819699e97239a26269/sample/text-styles-sample-simple.sketch -------------------------------------------------------------------------------- /sample/text-styles-sample.sketch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tbrasington/text-to-styles/d3e980b2f06330ddb62690819699e97239a26269/sample/text-styles-sample.sketch -------------------------------------------------------------------------------- /sample/tokens-array.json: -------------------------------------------------------------------------------- 1 | { 2 | "colours": { 3 | "Black": "rgba(0,0,0,1)", 4 | "Hot UI Blue": "rgba(0,88,255,1)", 5 | "Pinky Red": "rgba(254,84,105,1)", 6 | "Baby Green": "rgba(58,175,149,1)" 7 | }, 8 | "typography": [ 9 | { 10 | "name": "Style 1", 11 | "styles": { 12 | "fontFamily": "SF Pro Display", 13 | "fontWeight": 8, 14 | "fontSize": "28px", 15 | "lineHeight": "34px", 16 | "fontStyle": "normal", 17 | "paragraphSpacing": 25, 18 | "letterSpacing": "0.02em", 19 | "textTransform": "none" 20 | }, 21 | "alignments": [ 22 | "left", 23 | "right", 24 | "center" 25 | ], 26 | "adjustments": [ 27 | { 28 | "name": "BP3", 29 | "styles": { 30 | "fontSize": "42px", 31 | "lineHeight": "48px", 32 | "paragraphSpacing": 100, 33 | "letterSpacing": "0.03em" 34 | } 35 | } 36 | ] 37 | }, 38 | { 39 | "name": "Style 2", 40 | "styles": { 41 | "fontFamily": "Helvetica Neue", 42 | "fontWeight": 3, 43 | "fontSize": "14px", 44 | "lineHeight": "0px", 45 | "fontStyle": "normal", 46 | "paragraphSpacing": 0, 47 | "letterSpacing": "0em", 48 | "textTransform": "none" 49 | }, 50 | "alignments": [ 51 | "left", 52 | "right", 53 | "center" 54 | ], 55 | "adjustments": [] 56 | }, 57 | { 58 | "name": "Style 3", 59 | "styles": { 60 | "fontFamily": "Arial", 61 | "fontWeight": 9, 62 | "fontSize": "14px", 63 | "lineHeight": "0px", 64 | "fontStyle": "normal", 65 | "paragraphSpacing": 0, 66 | "letterSpacing": "0em", 67 | "textTransform": "none" 68 | }, 69 | "alignments": [ 70 | "left", 71 | "right", 72 | "center" 73 | ], 74 | "adjustments": [] 75 | }, 76 | { 77 | "name": "Style 4", 78 | "styles": { 79 | "fontFamily": "SF Pro Display", 80 | "fontWeight": 5, 81 | "fontSize": "14px", 82 | "lineHeight": "16px", 83 | "fontStyle": "normal", 84 | "paragraphSpacing": 20, 85 | "letterSpacing": "0.2em", 86 | "textTransform": "uppercase" 87 | }, 88 | "alignments": [ 89 | "left", 90 | "right", 91 | "center" 92 | ], 93 | "adjustments": [ 94 | { 95 | "name": "bp2", 96 | "styles": { 97 | "fontSize": "18px", 98 | "lineHeight": "20px", 99 | "letterSpacing": "0.5em" 100 | } 101 | }, 102 | { 103 | "name": "bp3", 104 | "styles": { 105 | "fontSize": "18px", 106 | "lineHeight": "40px", 107 | "letterSpacing": "0.5000625000000001em" 108 | } 109 | } 110 | ] 111 | }, 112 | { 113 | "name": "Style 5", 114 | "styles": { 115 | "fontFamily": "SF Pro Display", 116 | "fontWeight": 3, 117 | "fontSize": "200px", 118 | "lineHeight": "200px", 119 | "fontStyle": "normal", 120 | "paragraphSpacing": 200, 121 | "letterSpacing": "2em", 122 | "textTransform": "none" 123 | }, 124 | "alignments": [ 125 | "left", 126 | "right", 127 | "center" 128 | ], 129 | "adjustments": [] 130 | }, 131 | { 132 | "name": "Style 6", 133 | "styles": { 134 | "fontFamily": "SF Pro Display", 135 | "fontWeight": 3, 136 | "fontSize": "200px", 137 | "lineHeight": "200px", 138 | "fontStyle": "normal", 139 | "paragraphSpacing": 200, 140 | "letterSpacing": "2em", 141 | "textTransform": "none" 142 | }, 143 | "alignments": [ 144 | "left", 145 | "right", 146 | "center" 147 | ], 148 | "adjustments": [] 149 | }, 150 | { 151 | "name": "Style 7", 152 | "styles": { 153 | "fontFamily": "SF Pro Display", 154 | "fontWeight": 2, 155 | "fontSize": "200px", 156 | "lineHeight": "200px", 157 | "fontStyle": "normal", 158 | "paragraphSpacing": 200, 159 | "letterSpacing": "2em", 160 | "textTransform": "none" 161 | }, 162 | "alignments": [ 163 | "left", 164 | "right", 165 | "center" 166 | ], 167 | "adjustments": [] 168 | }, 169 | { 170 | "name": "Style 8", 171 | "styles": { 172 | "fontFamily": "SF Pro Display", 173 | "fontWeight": 5, 174 | "fontSize": "24px", 175 | "lineHeight": "32px", 176 | "fontStyle": "italic", 177 | "paragraphSpacing": 100, 178 | "letterSpacing": "0.1em", 179 | "textTransform": "none" 180 | }, 181 | "alignments": [ 182 | "left", 183 | "right", 184 | "center" 185 | ], 186 | "adjustments": [] 187 | } 188 | ] 189 | } -------------------------------------------------------------------------------- /sample/tokens-object.json: -------------------------------------------------------------------------------- 1 | { 2 | "colours": { 3 | "Black": "rgba(0,0,0,1)", 4 | "Hot UI Blue": "rgba(0,88,255,1)", 5 | "Pinky Red": "rgba(254,84,105,1)", 6 | "Baby Green": "rgba(58,175,149,1)" 7 | }, 8 | "typography": { 9 | "Style 1": { 10 | "name": "Style 1", 11 | "styles": { 12 | "fontFamily": "SFProDisplay-Semibold", 13 | "fontSize": "28px", 14 | "lineHeight": "34px", 15 | "fontWeight": 8, 16 | "paragraphSpacing": 25, 17 | "letterSpacing": "0.02em", 18 | "textTransform": "none" 19 | }, 20 | "alignments": [ 21 | "left", 22 | "right", 23 | "center" 24 | ], 25 | "adjustments": [ 26 | { 27 | "name": "BP3", 28 | "styles": { 29 | "fontSize": "42px", 30 | "lineHeight": "48px", 31 | "paragraphSpacing": 100, 32 | "letterSpacing": "0.03em" 33 | } 34 | } 35 | ] 36 | }, 37 | "Style 2": { 38 | "name": "Style 2", 39 | "styles": { 40 | "fontFamily": "SFProText-Regular", 41 | "fontSize": "14px", 42 | "lineHeight": "0px", 43 | "fontWeight": 5, 44 | "paragraphSpacing": 0, 45 | "letterSpacing": "0em", 46 | "textTransform": "none" 47 | }, 48 | "alignments": [ 49 | "left", 50 | "right", 51 | "center" 52 | ], 53 | "adjustments": [] 54 | }, 55 | "Style 3": { 56 | "name": "Style 3", 57 | "styles": { 58 | "fontFamily": "SFProText-Bold", 59 | "fontSize": "14px", 60 | "lineHeight": "0px", 61 | "fontWeight": 9, 62 | "paragraphSpacing": 0, 63 | "letterSpacing": "0em", 64 | "textTransform": "none" 65 | }, 66 | "alignments": [ 67 | "left", 68 | "right", 69 | "center" 70 | ], 71 | "adjustments": [] 72 | }, 73 | "Style 4": { 74 | "name": "Style 4", 75 | "styles": { 76 | "fontFamily": "SFProDisplay-Regular", 77 | "fontSize": "14px", 78 | "lineHeight": "16px", 79 | "fontWeight": 5, 80 | "paragraphSpacing": 20, 81 | "letterSpacing": "0.2em", 82 | "textTransform": "uppercase" 83 | }, 84 | "alignments": [ 85 | "left", 86 | "right", 87 | "center" 88 | ], 89 | "adjustments": [ 90 | { 91 | "name": "bp2", 92 | "styles": { 93 | "fontSize": "18px", 94 | "lineHeight": "20px", 95 | "letterSpacing": "0.5em" 96 | } 97 | }, 98 | { 99 | "name": "bp3", 100 | "styles": { 101 | "fontSize": "18px", 102 | "lineHeight": "40px", 103 | "letterSpacing": "0.5000625000000001em" 104 | } 105 | } 106 | ] 107 | }, 108 | "Style 5": { 109 | "name": "Style 5", 110 | "styles": { 111 | "fontFamily": "SFProDisplay-LightItalic", 112 | "fontSize": "200px", 113 | "lineHeight": "200px", 114 | "fontWeight": 3, 115 | "fontStyle": "italic", 116 | "paragraphSpacing": 500, 117 | "letterSpacing": "2em", 118 | "textTransform": "none" 119 | }, 120 | "alignments": [ 121 | "left", 122 | "right", 123 | "center" 124 | ], 125 | "adjustments": [] 126 | } 127 | } 128 | } -------------------------------------------------------------------------------- /src/createTextStyles.js: -------------------------------------------------------------------------------- 1 | import { splitEvery } from "ramda"; 2 | import sketch, { Text } from "sketch/dom"; 3 | import { extractStyles, generateTextStyles } from "./generators"; 4 | 5 | export default function(context) { 6 | const designTokens = extractStyles(context, false); 7 | 8 | const textStyles = generateTextStyles(designTokens); 9 | 10 | // all the pages are present so we can render the styles 11 | if (designTokens.render) { 12 | 13 | 14 | let document = sketch.fromNative(context.document); 15 | const cachedStyles = document.sharedTextStyles; 16 | 17 | let stored_styles = []; 18 | 19 | // const l = textStyles.length; 20 | // for (let property = 0; property < l; property++) { 21 | // let styleName = textStyles[property].name; 22 | // let checkStyle = cachedStyles.find(item => item.name === styleName); 23 | // if (typeof checkStyle === "object") { 24 | // const layer = new Text({ 25 | // style: textStyles[property].style 26 | // }); 27 | 28 | // checkStyle.style = layer.style; 29 | // stored_styles.push(checkStyle); 30 | // } else { 31 | // stored_styles.push({ 32 | // name: styleName, 33 | // style: textStyles[property].style 34 | // }); 35 | // } 36 | // } 37 | 38 | splitEvery(100, textStyles).forEach(styleChunk => { 39 | styleChunk.forEach(style => { 40 | let styleName = style.name; 41 | let checkStyle = cachedStyles.find(item => item.name === styleName); 42 | if (typeof checkStyle === "object") { 43 | const layer = new Text({ 44 | style: style.style 45 | }); 46 | 47 | checkStyle.style = layer.style; 48 | stored_styles.push(checkStyle); 49 | } else { 50 | stored_styles.push({ 51 | name: styleName, 52 | style: style.style 53 | }); 54 | } 55 | }); 56 | }); 57 | 58 | // update the shared text styles with this array 59 | document.sharedTextStyles = stored_styles; 60 | 61 | 62 | 63 | context.document.showMessage( 64 | `${Object.keys(textStyles).length} styles added (${ 65 | Object.keys(designTokens.typography).length 66 | } Text Styles * ${Object.keys(designTokens.colours).length} colours * ${ 67 | Object.keys(designTokens.textAlignments).length 68 | } alignments) 🙌` 69 | ); 70 | } else { 71 | context.document.showMessage( 72 | "No styles rendered. Check your document setup. Documentation here https://github.com/tbrasington/text-to-styles" 73 | ); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/generators.js: -------------------------------------------------------------------------------- 1 | 2 | import dom from "sketch/dom"; 3 | import UI from "sketch/ui"; 4 | 5 | function convertSketchColourToRGBA(colour) { 6 | const red = Math.round(colour.red() * 255); 7 | const green = Math.round(colour.green() * 255); 8 | const blue = Math.round(colour.blue() * 255); 9 | return "rgba(" + red + "," + green + "," + blue + "," + colour.alpha() + ")"; 10 | } 11 | 12 | export function extractStyles(context, convert) { 13 | const doc = context.document; 14 | const pages = doc.pages(); 15 | 16 | // we need to check if we have all the pages 17 | let pagesExist = { 18 | alignments: false, 19 | colors: false, 20 | styles: false 21 | }; 22 | 23 | // arrays and objects for our styles 24 | let TypographyStyles = []; 25 | let DocumentColours = {}; 26 | let textAlignments = []; 27 | 28 | pages.forEach(page => { 29 | // alignments 30 | if (String(page.name()) === "Alignments") { 31 | // page exists set to true 32 | pagesExist.alignments = true; 33 | 34 | page.layers().forEach(layer => { 35 | //log(layer.name() + ' ' + layer.textAlignment()) 36 | let alignment = "left"; 37 | if (layer.textAlignment() === 4) alignment = "left"; 38 | if (layer.textAlignment() === 2) alignment = "center"; 39 | if (layer.textAlignment() === 1) alignment = "right"; 40 | textAlignments.push(alignment); 41 | }); 42 | } 43 | 44 | // page styles 45 | if (String(page.name()) === "Styles") { 46 | // page exists set to true 47 | pagesExist.styles = true; 48 | 49 | // get styles 50 | page.layers().forEach(layer => { 51 | if (layer.class() === MSTextLayer) { 52 | // log(layer.font().fontName()) 53 | // log(layer.fontSize()) 54 | // log(layer.lineHeight()) 55 | // log(layer.characterSpacing()) 56 | // log(layer.style().textStyle().encodedAttributes() ) 57 | //log(layer.styleAttributes()["MSAttributedStringTextTransformAttribute"]) 58 | let textTransform = "none"; 59 | 60 | if ( 61 | String( 62 | layer.styleAttributes()[ 63 | "MSAttributedStringTextTransformAttribute" 64 | ] 65 | ) === "1" 66 | ) 67 | textTransform = "uppercase"; // null: none, 1: uppercase and 2 lowercase 68 | if ( 69 | String( 70 | layer.styleAttributes()[ 71 | "MSAttributedStringTextTransformAttribute" 72 | ] 73 | ) === "2" 74 | ) 75 | textTransform = "lowercase"; 76 | 77 | // console.log( String(layer.name()) + " " + layer.font().fontName() + " " + dom.fromNative(layer).style.fontWeight + " " + dom.fromNative(layer).style.fontStyle ) 78 | // console.log("-----" ) 79 | 80 | // fontFamily : dom.fromNative(layer).style.fontFamily , 81 | // fontWeight : dom.fromNative(layer).style.fontWeight , 82 | //console.log(dom.fromNative(layer).style.fontStyle) 83 | console.log(layer.sketchObject) 84 | 85 | function appKitWeightToCSSWeight(weight){ 86 | return [100,100,100,200,300,400,500,500,600,700,800,900,900,900,900,900][weight] 87 | } 88 | var weight = NSFontManager.sharedFontManager().weightOfFont_(layer.font()); 89 | log(appKitWeightToCSSWeight(weight)); 90 | 91 | TypographyStyles.push({ 92 | name: String(layer.name()), 93 | styles: { 94 | fontFamily: dom.fromNative(layer).style.fontFamily, 95 | fontWeight: convert 96 | ? dom.fromNative(layer).style.fontWeight * 100 97 | : dom.fromNative(layer).style.fontWeight, 98 | fontSize: layer.fontSize() + (convert ? "px" : ""), 99 | lineHeight: layer.lineHeight() + (convert ? "px" : ""), 100 | fontStyle: 101 | dom.fromNative(layer).style.fontStyle != undefined 102 | ? dom.fromNative(layer).style.fontStyle 103 | : "normal", 104 | paragraphSpacing: dom.fromNative(layer).style.paragraphSpacing, 105 | ...(convert && { 106 | letterSpacing: String(layer.characterSpacing() / 10 + "em") 107 | }), 108 | ...(!convert && { kerning: layer.characterSpacing() }), 109 | textTransform: textTransform, 110 | borders: dom.fromNative(layer).style.borders || [] 111 | }, 112 | alignments: textAlignments, 113 | adjustments: [] 114 | }); 115 | } 116 | }); 117 | } 118 | 119 | // get colours 120 | if (String(page.name()) === "Colours" || String(page.name()) === "Colors") { 121 | // page exists set to true 122 | pagesExist.colors = true; 123 | 124 | DocumentColours = {}; 125 | page.layers().forEach(layer => { 126 | DocumentColours[layer.name()] = convert 127 | ? convertSketchColourToRGBA( 128 | layer 129 | .style() 130 | .firstEnabledFill() 131 | .color() 132 | ) 133 | : layer 134 | .style() 135 | .firstEnabledFill() 136 | .color(); 137 | }); 138 | } 139 | }); 140 | 141 | 142 | let allPagesHere = true; 143 | let messages = []; 144 | 145 | // first check for pages 146 | if (!pagesExist.alignments) { 147 | messages.push('Your document is missing the page named "Alignments".'); 148 | allPagesHere = false; 149 | } 150 | 151 | if (!pagesExist.colors) { 152 | messages.push('Your document is missing the page named "Colors". '); 153 | allPagesHere = false; 154 | } 155 | 156 | if (!pagesExist.styles) { 157 | messages.push('Your document is missing the page named "Styles".'); 158 | allPagesHere = false; 159 | } 160 | 161 | // now that we have checked for pages, lets see if there is anything in them 162 | if (pagesExist.alignments && textAlignments.length === 0) { 163 | messages.push("Your alignments page has no alignments in it."); 164 | allPagesHere = false; 165 | } 166 | 167 | if (pagesExist.colors && Object.keys(DocumentColours).length === 0) { 168 | messages.push("Your colors page has no colors in it."); 169 | allPagesHere = false; 170 | } 171 | 172 | if (pagesExist.styles && TypographyStyles.length === 0) { 173 | messages.push("Your styles page has no styles in it."); 174 | allPagesHere = false; 175 | } 176 | 177 | let messageString = ""; 178 | messages.forEach(message => { 179 | messageString += message + "\n"; 180 | }); 181 | 182 | if (!allPagesHere) UI.alert("Unable to render styles", messageString); 183 | 184 | const DesignSystemTokens = { 185 | colours: DocumentColours, 186 | typography: TypographyStyles, 187 | textAlignments: textAlignments, 188 | render: allPagesHere 189 | }; 190 | 191 | return DesignSystemTokens; 192 | } 193 | 194 | export function generateTextStyles(json) { 195 | // can we calculate this beforehand? 196 | let typeStyles = new Array(); 197 | 198 | json.typography.forEach(item => { 199 | Object.keys(json.colours).forEach(colour => { 200 | item.alignments.map((align, index) => { 201 | // this splits at a slash and adds the adjustments for breakpoints after the alignment 202 | // assumption is that there is only one adjusment 203 | let name = item.name.split("/"); 204 | typeStyles.push({ 205 | name: `${name[0]}/${colour}/${index + 206 | "_" + 207 | align + 208 | (name.length > 1 ? "/" + name[1] : "")}`, 209 | style: { 210 | textColor: dom.Style.colorToString(json.colours[colour]), 211 | alignment: align, 212 | ...item.styles 213 | } 214 | }); 215 | }); 216 | }); 217 | }); 218 | 219 | return typeStyles; 220 | } 221 | 222 | function checkMatch(baseStyle, newStyle, prop) { 223 | let value = true; 224 | 225 | // for now we are just going off the previous style. As this would need to check 226 | // every prop across every adjustment :/ 227 | if (JSON.stringify(baseStyle[prop]) === JSON.stringify(newStyle[prop])) { 228 | // very primitive and breaks if order is out of sync 229 | value = false; 230 | } 231 | 232 | //log( prop + ' ' + baseStyle[prop] + ' ----- ' + prop + ' ' +newStyle[prop] + ' value ' + value) 233 | 234 | return value; 235 | } 236 | 237 | export function generateJSONStyles(json, arrayFormat) { 238 | let typeStyles = {}; 239 | let refinedBreakpoints = []; 240 | //log(json.typography) 241 | json.typography.forEach(item => { 242 | let name = item.name.split("/"); 243 | if (!typeStyles[name[0]]) { 244 | typeStyles[name[0]] = { 245 | name: name[0], 246 | styles: item.styles, 247 | alignments: item.alignments, 248 | adjustments: [] 249 | }; 250 | } else { 251 | let currentStyle = item.styles; 252 | //let previousStyle = typeStyles[name[0]].styles 253 | // work out previous style 254 | const adjustmentLength = typeStyles[name[0]].adjustments.length; 255 | refinedBreakpoints[adjustmentLength] = { name: name[1], styles: {} }; 256 | 257 | // if(adjustmentLength>0){ 258 | // previousStyle = typeStyles[name[0]].adjustments[adjustmentLength-1] 259 | // } 260 | 261 | //previousStyle, 262 | Object.keys(currentStyle).map(checked => { 263 | if (checkMatch(typeStyles[name[0]].styles, currentStyle, checked)) { 264 | refinedBreakpoints[adjustmentLength].styles[checked] = 265 | currentStyle[checked]; 266 | } 267 | }); 268 | 269 | typeStyles[name[0]].adjustments.push( 270 | refinedBreakpoints[adjustmentLength] 271 | ); 272 | } 273 | }); 274 | 275 | // finally merge colours back in and return text to an array 276 | let formattedTokens = { 277 | colours: json.colours, 278 | typography: arrayFormat 279 | ? Object.keys(typeStyles).map(function(key) { 280 | return typeStyles[key]; 281 | }) 282 | : typeStyles 283 | }; 284 | return formattedTokens; 285 | } 286 | -------------------------------------------------------------------------------- /src/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "compatibleVersion": 3, 3 | "bundleVersion": 1, 4 | "icon": "icon.png", 5 | "name": "Text to Styles", 6 | "description": "Generates Sketch Text Styles from Layers to easily manage, size, colour and alignment variations", 7 | "commands": [ 8 | 9 | { 10 | "name": "Create Text Styles", 11 | "identifier": "createTextStyles", 12 | "script": "./createTextStyles.js" 13 | } 14 | ], 15 | "menu": { 16 | "title": "Text to Styles", 17 | "items": [ 18 | "runDocumentSetup", 19 | "createTextStyles", 20 | "renderStyleSheet", 21 | "renderJSON" 22 | ] 23 | } 24 | } -------------------------------------------------------------------------------- /src/maninfest.all.json: -------------------------------------------------------------------------------- 1 | { 2 | "compatibleVersion": 3, 3 | "bundleVersion": 1, 4 | "icon": "icon.png", 5 | "name": "Text to Styles", 6 | "description": "Generates Sketch Text Styles from Layers to easily manage, size, colour and alignment variations", 7 | "commands": [ 8 | { 9 | "name": "Run Document Setup", 10 | "identifier": "runDocumentSetup", 11 | "script": "./setupDocument.js" 12 | }, 13 | { 14 | "name": "Create Text Styles", 15 | "identifier": "createTextStyles", 16 | "script": "./createTextStyles.js" 17 | }, 18 | { 19 | "name": "Render Style Sheet", 20 | "identifier": "renderStyleSheet", 21 | "script": "./renderStyleSheet.js" 22 | }, 23 | { 24 | "name": "Export JSON Tokens", 25 | "identifier": "renderJSON", 26 | "script": "./renderJSON.js" 27 | } 28 | ], 29 | "menu": { 30 | "title": "Text to Styles", 31 | "items": [ 32 | "runDocumentSetup", 33 | "createTextStyles", 34 | "renderStyleSheet", 35 | "renderJSON" 36 | ] 37 | } 38 | } -------------------------------------------------------------------------------- /src/renderJSON.js: -------------------------------------------------------------------------------- 1 | import dialog from "@skpm/dialog"; 2 | import path from "@skpm/path"; 3 | import fs from "@skpm/fs"; 4 | import jsonFormat from "json-format"; 5 | import Sketch from "sketch"; 6 | 7 | import { extractStyles, generateJSONStyles } from "./generators"; 8 | 9 | function save(filename, fileContents) { 10 | console.log(filename) 11 | 12 | const targetFile = path.resolve(filename); 13 | fs.writeFileSync(targetFile, fileContents, "utf8"); 14 | } 15 | 16 | export default function(context) { 17 | const doc = context.document; 18 | 19 | // ok lets get the styles 20 | 21 | const designTokens = extractStyles(context, true); 22 | 23 | if (designTokens.render) { 24 | const options = ["Array", "Object"]; 25 | const textSaveSelection = Sketch.UI.getInputFromUser( 26 | "Would you like the text styles as an Array or Object", 27 | { 28 | type: Sketch.UI.INPUT_TYPE.selection, 29 | possibleValues: options 30 | }, 31 | (err, value) => { 32 | if (err) { 33 | // most likely the user canceled the input 34 | return; 35 | } else { 36 | if (value[2]) { 37 | let textSaveMethod = true; 38 | if (value === "Object") textSaveMethod = false; 39 | if (value === "Array") textSaveMethod = true; 40 | const arranged = generateJSONStyles(designTokens, textSaveMethod); 41 | 42 | // Save the file 43 | let saveDialog = dialog.showSaveDialog( 44 | doc, 45 | { 46 | defaultPath: "tokens.json", 47 | message: "Choose a folder to save your tokens" 48 | } 49 | ).then( result => { 50 | if(!result.cancelled) save(result.filePath, jsonFormat(arranged)); 51 | }); 52 | 53 | 54 | } 55 | } 56 | } 57 | ); 58 | } else { 59 | context.document.showMessage('No styles rendered. Check your document setup. Documentation here https://github.com/tbrasington/text-to-styles'); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/renderStyleSheet.js: -------------------------------------------------------------------------------- 1 | import sketch, { Text } from "sketch/dom"; 2 | 3 | export default function(context) { 4 | let document = sketch.fromNative(context.document); 5 | const pages = context.document.pages(); 6 | 7 | // Remove previous rendered pages (thanks to react-sketchapp) 8 | for (let index = pages.length - 1; index >= 0; index -= 1) { 9 | if (pages.length > 1) { 10 | String(pages[index].name()) === "Rendered Styles" && 11 | context.document.documentData().removePageAtIndex(index); 12 | } 13 | } 14 | 15 | let RenderPage = context.document.addBlankPage(); 16 | RenderPage.name = "Rendered Styles"; 17 | 18 | let previousFrame = null; 19 | // now make a page 20 | 21 | const stl = document.sharedTextStyles.length; 22 | for (let property = 0; property < stl; property++) { 23 | let textLayer = new Text({ 24 | text: document.sharedTextStyles[property].name, 25 | frame: { 26 | width:100, 27 | height: 32, 28 | x: 0, 29 | y: 30 | previousFrame != null 31 | ? Math.ceil(previousFrame.frame.height + previousFrame.frame.y + 24) 32 | : 0 33 | }, 34 | sharedStyleId: document.sharedTextStyles[property].id, 35 | style: document.sharedTextStyles[property].style, 36 | parent : RenderPage 37 | }); 38 | 39 | textLayer.name = document.sharedTextStyles[property].name; 40 | previousFrame = textLayer; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/setupDocument.js: -------------------------------------------------------------------------------- 1 | import { Text, ShapePath, Style } from "sketch/dom"; 2 | 3 | export default function(context) { 4 | 5 | // Remove exisiting Pages 6 | for (let index = context.document.pages().length - 1; index >= 0; index -= 1) { 7 | 8 | context.document.documentData().removePageAtIndex(index); 9 | 10 | } 11 | 12 | // Add pages 13 | const StylesPage = context.document.addBlankPage(); 14 | StylesPage.name = "Styles"; 15 | 16 | const AlignmentsPage = context.document.addBlankPage(); 17 | AlignmentsPage.name = "Alignments"; 18 | 19 | const ColorsPage = context.document.addBlankPage(); 20 | ColorsPage.name = "Colors"; 21 | 22 | 23 | // Add Text to Styles Page 24 | const textLayer1 = new Text({ 25 | text: "Style 1", 26 | frame: { 27 | width: 100, 28 | height: 32, 29 | x: 0, 30 | y: 0 31 | }, 32 | style: { 33 | fontFamily: 'Helvetica', 34 | fontWeight: 8, 35 | fontSize: 24, 36 | lineHeight: 24 * 1.5, 37 | fontStyle: "normal", 38 | paragraphSpacing: 24*1.5, 39 | kerning : 0 40 | }, 41 | parent : StylesPage 42 | }); 43 | textLayer1.name = "Style 1"; 44 | 45 | const textLayer2 = new Text({ 46 | text: "Style 2", 47 | frame: { 48 | width: 100, 49 | height: 32, 50 | x: 0, 51 | y: textLayer1.frame.height + 24 52 | }, 53 | style: { 54 | fontFamily: 'Helvetica', 55 | fontWeight: 4, 56 | fontSize: 16, 57 | lineHeight: 16 * 1.5, 58 | fontStyle: "normal", 59 | paragraphSpacing: 16*1.5, 60 | kerning : 0 61 | }, 62 | parent : StylesPage 63 | }); 64 | textLayer2.name = "Style 2"; 65 | 66 | // Add Alignments 67 | const alignmentLayerLeft = new Text({ 68 | text: "Left", 69 | frame: { 70 | width: 100, 71 | height: 32, 72 | x: 0, 73 | y: 0 74 | }, 75 | style: { 76 | fontFamily: 'Helvetica', 77 | fontWeight: 4, 78 | fontSize: 16, 79 | lineHeight: 16 * 1.5, 80 | fontStyle: "normal", 81 | paragraphSpacing: 16*1.5, 82 | kerning : 0, 83 | alignment : 'left' 84 | }, 85 | parent : AlignmentsPage 86 | }); 87 | alignmentLayerLeft.name = "Left"; 88 | 89 | const alignmentLayerRight = new Text({ 90 | text: "Right", 91 | frame: { 92 | width: 100, 93 | height: 32, 94 | x: 0, 95 | y: 0 96 | }, 97 | style: { 98 | fontFamily: 'Helvetica', 99 | fontWeight: 4, 100 | fontSize: 16, 101 | lineHeight: 16 * 1.5, 102 | fontStyle: "normal", 103 | paragraphSpacing: 16*1.5, 104 | kerning : 0, 105 | alignment : 'right' 106 | }, 107 | parent : AlignmentsPage 108 | }); 109 | alignmentLayerLeft.name = "Right"; 110 | 111 | // Add Colors 112 | new ShapePath({ 113 | name: 'Black', 114 | shapeType: ShapePath.ShapeType.Oval, 115 | parent : ColorsPage, 116 | frame : { 117 | width: 100, 118 | height: 100, 119 | x: 0, 120 | y : 0 121 | }, 122 | style : { 123 | fills : [ 124 | { 125 | color: '#111111', 126 | fillType: Style.FillType.Color, 127 | } 128 | ], 129 | borders : [] 130 | } 131 | }); 132 | 133 | new ShapePath({ 134 | name: 'Red', 135 | shapeType: ShapePath.ShapeType.Oval, 136 | parent : ColorsPage, 137 | frame : { 138 | width: 100, 139 | height: 100, 140 | x: 124, 141 | y : 0 142 | }, 143 | style : { 144 | fills : [ 145 | { 146 | color: '#cc0000', 147 | fillType: Style.FillType.Color, 148 | } 149 | ], 150 | borders : [] 151 | } 152 | }); 153 | 154 | } 155 | -------------------------------------------------------------------------------- /text-to-styles.sketchplugin/Contents/Resources/icon-64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tbrasington/text-to-styles/d3e980b2f06330ddb62690819699e97239a26269/text-to-styles.sketchplugin/Contents/Resources/icon-64.png -------------------------------------------------------------------------------- /text-to-styles.sketchplugin/Contents/Resources/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tbrasington/text-to-styles/d3e980b2f06330ddb62690819699e97239a26269/text-to-styles.sketchplugin/Contents/Resources/icon.png -------------------------------------------------------------------------------- /text-to-styles.sketchplugin/Contents/Sketch/__renderJSON.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["webpack://exports/webpack/bootstrap","webpack://exports/./node_modules/@skpm/dialog/lib/index.js","webpack://exports/./node_modules/@skpm/dialog/lib/message-box.js","webpack://exports/./node_modules/@skpm/dialog/lib/open-dialog.js","webpack://exports/./node_modules/@skpm/dialog/lib/save-dialog.js","webpack://exports/./node_modules/@skpm/dialog/lib/utils.js","webpack://exports/./node_modules/@skpm/fs/index.js","webpack://exports/./node_modules/@skpm/path/index.js","webpack://exports/./node_modules/@skpm/path/sketch-specifics.js","webpack://exports/./node_modules/@skpm/promise/index.js","webpack://exports/./node_modules/json-format/index.js","webpack://exports/./src/generators.js","webpack://exports/./src/renderJSON.js","webpack://exports/external \"sketch\"","webpack://exports/external \"sketch/dom\"","webpack://exports/external \"sketch/ui\"","webpack://exports/external \"util\""],"names":["convertSketchColourToRGBA","colour","red","Math","round","green","blue","alpha","extractStyles","context","convert","doc","document","pages","pagesExist","alignments","colors","styles","TypographyStyles","DocumentColours","textAlignments","forEach","page","String","name","layers","layer","alignment","textAlignment","push","class","MSTextLayer","textTransform","styleAttributes","fontFamily","dom","fromNative","style","fontWeight","fontSize","lineHeight","fontStyle","undefined","paragraphSpacing","letterSpacing","characterSpacing","kerning","borders","adjustments","firstEnabledFill","color","allPagesHere","messages","length","Object","keys","messageString","message","UI","alert","DesignSystemTokens","colours","typography","render","generateTextStyles","json","typeStyles","Array","item","map","align","index","split","textColor","Style","colorToString","checkMatch","baseStyle","newStyle","prop","value","JSON","stringify","generateJSONStyles","arrayFormat","refinedBreakpoints","currentStyle","adjustmentLength","checked","formattedTokens","key","save","filename","fileContents","console","log","targetFile","path","resolve","fs","writeFileSync","designTokens","options","textSaveSelection","Sketch","getInputFromUser","type","INPUT_TYPE","selection","possibleValues","err","textSaveMethod","arranged","saveDialog","dialog","showSaveDialog","defaultPath","then","result","cancelled","filePath","jsonFormat","showMessage"],"mappings":";;;;;;;;QAAA;QACA;;QAEA;QACA;;QAEA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;;QAEA;QACA;;QAEA;QACA;;QAEA;QACA;QACA;;;QAGA;QACA;;QAEA;QACA;;QAEA;QACA;QACA;QACA,0CAA0C,gCAAgC;QAC1E;QACA;;QAEA;QACA;QACA;QACA,wDAAwD,kBAAkB;QAC1E;QACA,iDAAiD,cAAc;QAC/D;;QAEA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA,yCAAyC,iCAAiC;QAC1E,gHAAgH,mBAAmB,EAAE;QACrI;QACA;;QAEA;QACA;QACA;QACA,2BAA2B,0BAA0B,EAAE;QACvD,iCAAiC,eAAe;QAChD;QACA;QACA;;QAEA;QACA,sDAAsD,+DAA+D;;QAErH;QACA;;;QAGA;QACA;;;;;;;;;;;;AClFA;AACA;;AAEA;AACA,kBAAkB,mBAAO,CAAC,qEAAe;AACzC,sBAAsB,mBAAO,CAAC,qEAAe;AAC7C,kBAAkB,mBAAO,CAAC,qEAAe;AACzC,sBAAsB,mBAAO,CAAC,qEAAe;AAC7C,kBAAkB,mBAAO,CAAC,qEAAe;AACzC,sBAAsB,mBAAO,CAAC,qEAAe;AAC7C;AACA;;;;;;;;;;;;ACXA;AACA,YAAY,mBAAO,CAAC,yDAAS;;AAE7B;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;;AAEA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;;;;;;;;;;;;ACnIA;AACA,YAAY,mBAAO,CAAC,yDAAS;;AAE7B;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA,OAAO;AACP,KAAK;;AAEL;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,qBAAqB,iBAAiB;AACtC;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,qBAAqB,iBAAiB;AACtC;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;;;;;;;;;;;;AClHA;AACA,YAAY,mBAAO,CAAC,yDAAS;;AAE7B;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA,OAAO;AACP,KAAK;;AAEL;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;;;;;;;;;;;;ACrGA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS;AACT;AACA;AACA;AACA;AACA;AACA,SAAS;AACT;AACA;AACA,OAAO;AACP;AACA,GAAG;AACH;;AAEA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;;AAEA;AACA;AACA;AACA;;;;;;;;;;;;;AC9DA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA,GAAG;;AAEH;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;;AAEH;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA,mBAAmB,OAAO;AAC1B;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA,iBAAiB,kBAAkB;AACnC;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,+BAA+B,sDAAsD;AACrF,mCAAmC,0DAA0D;AAC7F,6BAA6B,mDAAmD;AAChF,wBAAwB,eAAe;AACvC,wBAAwB,iDAAiD;AACzE,0BAA0B,gDAAgD;AAC1E,gCAAgC,sDAAsD;AACtF;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA,GAAG;;AAEH;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;;;;;;;;;;;AC/UA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA,sBAAsB,mBAAO,CAAC,yEAAoB;;AAElD;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB,kBAAkB;AACnC;AACA;AACA;AACA;AACA;AACA;AACA,OAAO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,eAAe;AACf;AACA;AACA;AACA;AACA;AACA;AACA;AACA,WAAW;AACX;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO;AACP;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA,KAAK;AACL;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA,sCAAsC,8BAA8B;AACpE;AACA;AACA;AACA,OAAO;AACP;AACA;AACA;AACA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA,KAAK;AACL;AACA,KAAK;AACL;AACA;AACA,GAAG;;AAEH;AACA;;AAEA;;AAEA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA,GAAG;;AAEH;AACA;AACA;AACA,GAAG;;AAEH;AACA;AACA;AACA,mBAAmB,sBAAsB;AACzC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;;AAEH;AACA;AACA;;AAEA;;AAEA;AACA;;AAEA;;AAEA;AACA;AACA,UAAU,yBAAyB;AACnC;AACA;AACA;AACA;;AAEA;AACA;AACA,UAAU,qBAAqB;AAC/B;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA,UAAU,aAAa;AACvB;AACA;AACA;AACA;AACA,4CAA4C;AAC5C;AACA,WAAW;AACX;AACA,qCAAqC;AACrC;AACA;AACA,SAAS;AACT;AACA;AACA,gDAAgD;AAChD;AACA,WAAW;AACX;AACA,wCAAwC;AACxC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,2CAA2C,cAAc;AACzD;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;;AAEH;AACA;AACA;AACA,GAAG;;AAEH;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iCAAiC,QAAQ;AACzC;AACA;AACA;AACA;AACA;AACA;AACA,OAAO;AACP;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,GAAG;;AAEH;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA,+BAA+B,QAAQ;AACvC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS;AACT;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa;AACb;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,KAAK;AACL,+BAA+B,QAAQ;AACvC;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS;AACT;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,GAAG;;AAEH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iCAAiC,QAAQ;AACzC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO;AACP;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;;AAEH;AACA;AACA;AACA;AACA;AACA,GAAG;;AAEH;AACA;;AAEA,eAAe;AACf;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA,UAAU,YAAY;AACtB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO;AACP;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA,OAAO;AACP;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA,GAAG;;AAEH;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;;;;;;;;;;;;AC3gBA,WAAW,mBAAO,CAAC,kBAAM;;AAEzB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;AC7BA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO;AACP,KAAK;AACL;AACA;AACA;AACA,OAAO;AACP;AACA;AACA;;AAEA;;AAEA;AACA;AACA,WAAW,SAAS;AACpB;AACA;AACA;AACA;AACA;AACA,aAAa,QAAQ;AACrB;AACA,aAAa,SAAS;AACtB;AACA,aAAa,kBAAkB;AAC/B;AACA,aAAa,kBAAkB;AAC/B;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA;AACA,GAAG;AACH;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;;AAEA,+CAA+C,SAAS;AACxD;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,eAAe;AACf;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO;AACP;AACA;AACA;;AAEA,mBAAmB,iBAAiB;AACpC;AACA;AACA,GAAG;AACH;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA,GAAG;AACH;;AAEA;AACA;AACA;AACA,GAAG;AACH;;AAEA;AACA;AACA;AACA;AACA;;AAEA,qCAAqC,SAAS;AAC9C;AACA;AACA,GAAG;AACH;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;;;;;;;;;;;;AC9QA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA,UAAU,sBAAsB;AAChC,YAAY;AACZ,GAAG;AACH;AACA;AACA,GAAG;AACH,wBAAwB,kCAAkC,EAAE;AAC5D,0BAA0B,gBAAgB;AAC1C,uCAAuC,kDAAkD;;AAEzF;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA,0B;;AAEA;AACA,iBAAiB,iBAAiB;AAClC;;AAEA;AACA,aAAa;AACb;AACA;AACA;AACA,aAAa;AACb;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,c;AACA,K;AACA;;AAEA;AACA;AACA;AACA,4CAA4C,4BAA4B,EAAE;AAC1E;AACA,kCAAkC;;AAElC;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;;;;;;;;;;;;;;AC5EA;AACA;;AAEA,SAASA,yBAAT,CAAmCC,MAAnC,EAA2C;AACzC,MAAMC,GAAG,GAAGC,IAAI,CAACC,KAAL,CAAWH,MAAM,CAACC,GAAP,KAAe,GAA1B,CAAZ;AACA,MAAMG,KAAK,GAAGF,IAAI,CAACC,KAAL,CAAWH,MAAM,CAACI,KAAP,KAAiB,GAA5B,CAAd;AACA,MAA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","file":"__renderJSON.js","sourcesContent":[" \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n \t\t}\n \t};\n\n \t// define __esModule on exports\n \t__webpack_require__.r = function(exports) {\n \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n \t\t}\n \t\tObject.defineProperty(exports, '__esModule', { value: true });\n \t};\n\n \t// create a fake namespace object\n \t// mode & 1: value is a module id, require it\n \t// mode & 2: merge all properties of value into the ns\n \t// mode & 4: return value when already ns object\n \t// mode & 8|1: behave like require\n \t__webpack_require__.t = function(value, mode) {\n \t\tif(mode & 1) value = __webpack_require__(value);\n \t\tif(mode & 8) return value;\n \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n \t\tvar ns = Object.create(null);\n \t\t__webpack_require__.r(ns);\n \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n \t\treturn ns;\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = \"./src/renderJSON.js\");\n","/* let's try to match the API from Electron's Dialog\n(https://github.com/electron/electron/blob/master/docs/api/dialog.md) */\n\nmodule.exports = {\n showOpenDialog: require('./open-dialog').openDialog,\n showOpenDialogSync: require('./open-dialog').openDialogSync,\n showSaveDialog: require('./save-dialog').saveDialog,\n showSaveDialogSync: require('./save-dialog').saveDialogSync,\n showMessageBox: require('./message-box').messageBox,\n showMessageBoxSync: require('./message-box').messageBoxSync,\n // showErrorBox: require('./error-box'),\n}\n","/* eslint-disable no-not-accumulator-reassign/no-not-accumulator-reassign */\nvar utils = require('./utils')\n\nvar typeMap = {\n none: 0,\n info: 1,\n error: 2,\n question: 1,\n warning: 2,\n}\n\nfunction setupOptions(document, options) {\n if (\n !document ||\n (typeof document.isKindOfClass !== 'function' && !document.sketchObject)\n ) {\n options = document\n document = undefined\n } else if (document.sketchObject) {\n document = document.sketchObject\n }\n if (!options) {\n options = {}\n }\n\n var dialog = NSAlert.alloc().init()\n\n if (options.type) {\n dialog.alertStyle = typeMap[options.type] || 0\n }\n\n if (options.buttons && options.buttons.length) {\n options.buttons.forEach(function addButton(button) {\n dialog.addButtonWithTitle(\n options.normalizeAccessKeys ? button.replace(/&/g, '') : button\n )\n // TODO: add keyboard shortcut if options.normalizeAccessKeys\n })\n }\n\n if (typeof options.defaultId !== 'undefined') {\n var buttons = dialog.buttons()\n if (options.defaultId < buttons.length) {\n // Focus the button at defaultId if the user opted to do so.\n // The first button added gets set as the default selected.\n // So remove that default, and make the requested button the default.\n buttons[0].setKeyEquivalent('')\n buttons[options.defaultId].setKeyEquivalent('\\r')\n }\n }\n\n if (options.title) {\n // not shown on macOS\n }\n\n if (options.message) {\n dialog.messageText = options.message\n }\n\n if (options.detail) {\n dialog.informativeText = options.detail\n }\n\n if (options.checkboxLabel) {\n dialog.showsSuppressionButton = true\n dialog.suppressionButton().title = options.checkboxLabel\n\n if (typeof options.checkboxChecked !== 'undefined') {\n dialog.suppressionButton().state = options.checkboxChecked\n ? NSOnState\n : NSOffState\n }\n }\n\n if (options.icon) {\n if (typeof options.icon === 'string') {\n options.icon = NSImage.alloc().initWithContentsOfFile(options.icon)\n }\n dialog.icon = options.icon\n } else if (\n typeof __command !== 'undefined' &&\n __command.pluginBundle() &&\n __command.pluginBundle().icon()\n ) {\n dialog.icon = __command.pluginBundle().icon()\n } else {\n var icon = NSImage.imageNamed('plugins')\n if (icon) {\n dialog.icon = icon\n }\n }\n\n return {\n document: document,\n options: options,\n dialog: dialog,\n }\n}\n\n// https://github.com/electron/electron/blob/master/docs/api/dialog.md#dialogshowmessageboxbrowserwindow-options\nmodule.exports.messageBox = function messageBox(document, options) {\n var setup = setupOptions(document, options)\n\n return utils.runDialog(\n setup.dialog,\n function getResult(_dialog, returnCode) {\n return {\n response:\n setup.options.buttons && setup.options.buttons.length\n ? Number(returnCode) - 1000\n : Number(returnCode),\n checkboxChecked: _dialog.suppressionButton().state() == NSOnState,\n }\n },\n setup.document\n )\n}\n\n// https://github.com/electron/electron/blob/master/docs/api/dialog.md#dialogshowmessageboxsyncbrowserwindow-options\nmodule.exports.messageBoxSync = function messageBoxSync(document, options) {\n var setup = setupOptions(document, options)\n\n return utils.runDialogSync(\n setup.dialog,\n function getResult(_dialog, returnCode) {\n return setup.options.buttons && setup.options.buttons.length\n ? Number(returnCode) - 1000\n : Number(returnCode)\n },\n setup.document\n )\n}\n","/* eslint-disable no-not-accumulator-reassign/no-not-accumulator-reassign */\nvar utils = require('./utils')\n\nfunction setupOptions(document, options) {\n if (\n !document ||\n (typeof document.isKindOfClass !== 'function' && !document.sketchObject)\n ) {\n options = document\n document = undefined\n }\n if (!options) {\n options = {}\n }\n\n var dialog = NSOpenPanel.openPanel()\n\n if (options.title) {\n dialog.title = options.title\n }\n\n if (options.defaultPath) {\n dialog.setDirectoryURL(utils.getURL(options.defaultPath))\n }\n\n if (options.buttonLabel) {\n dialog.prompt = options.buttonLabel\n }\n\n if (options.filters && options.filters.length) {\n var exts = []\n options.filters.forEach(function setFilter(filter) {\n filter.extensions.forEach(function setExtension(ext) {\n exts.push(ext)\n })\n })\n\n dialog.allowedFileTypes = exts\n }\n\n var hasProperty =\n Array.isArray(options.properties) && options.properties.length > 0\n dialog.canChooseFiles =\n hasProperty && options.properties.indexOf('openFile') !== -1\n dialog.canChooseDirectories =\n hasProperty && options.properties.indexOf('openDirectory') !== -1\n dialog.allowsMultipleSelection =\n hasProperty && options.properties.indexOf('multiSelections') !== -1\n dialog.showsHiddenFiles =\n hasProperty && options.properties.indexOf('showHiddenFiles') !== -1\n dialog.canCreateDirectories =\n hasProperty && options.properties.indexOf('createDirectory') !== -1\n dialog.resolvesAliases =\n !hasProperty || options.properties.indexOf('noResolveAliases') === -1\n dialog.treatsFilePackagesAsDirectories =\n hasProperty && options.properties.indexOf('treatPackageAsDirectory') !== -1\n\n if (options.message) {\n dialog.message = options.message\n }\n\n return {\n document: document,\n options: options,\n dialog: dialog,\n }\n}\n\n// https://github.com/electron/electron/blob/master/docs/api/dialog.md#dialogshowopendialogbrowserwindow-options\nmodule.exports.openDialog = function openDialog(document, options) {\n var setup = setupOptions(document, options)\n\n return utils.runDialog(\n setup.dialog,\n function getResult(_dialog, returnCode) {\n if (returnCode != NSOKButton) {\n return {\n canceled: true,\n filePaths: [],\n }\n }\n var result = []\n var urls = _dialog.URLs()\n for (var k = 0; k < urls.length; k += 1) {\n result.push(String(urls[k].path()))\n }\n return {\n canceled: false,\n filePaths: result,\n }\n },\n setup.document\n )\n}\n\n// https://github.com/electron/electron/blob/master/docs/api/dialog.md#dialogshowopendialogsyncbrowserwindow-options\nmodule.exports.openDialogSync = function openDialogSync(document, options) {\n var setup = setupOptions(document, options)\n\n return utils.runDialogSync(\n setup.dialog,\n function getResult(_dialog, returnCode) {\n if (returnCode != NSOKButton) {\n return []\n }\n var result = []\n var urls = _dialog.URLs()\n for (var k = 0; k < urls.length; k += 1) {\n result.push(String(urls[k].path()))\n }\n return result\n },\n setup.document\n )\n}\n","/* eslint-disable no-not-accumulator-reassign/no-not-accumulator-reassign */\nvar utils = require('./utils')\n\nfunction setupOptions(document, options) {\n if (\n !document ||\n (typeof document.isKindOfClass !== 'function' && !document.sketchObject)\n ) {\n options = document\n document = undefined\n }\n if (!options) {\n options = {}\n }\n\n var dialog = NSSavePanel.savePanel()\n\n if (options.title) {\n dialog.title = options.title\n }\n\n if (options.defaultPath) {\n // that's a path\n dialog.setDirectoryURL(utils.getURL(options.defaultPath))\n\n if (\n options.defaultPath[0] === '.' ||\n options.defaultPath[0] === '~' ||\n options.defaultPath[0] === '/'\n ) {\n var parts = options.defaultPath.split('/')\n if (parts.length > 1 && parts[parts.length - 1]) {\n dialog.setNameFieldStringValue(parts[parts.length - 1])\n }\n } else {\n dialog.setNameFieldStringValue(options.defaultPath)\n }\n }\n\n if (options.buttonLabel) {\n dialog.prompt = options.buttonLabel\n }\n\n if (options.filters && options.filters.length) {\n var exts = []\n options.filters.forEach(function setFilter(filter) {\n filter.extensions.forEach(function setExtension(ext) {\n exts.push(ext)\n })\n })\n\n dialog.allowedFileTypes = exts\n }\n\n if (options.message) {\n dialog.message = options.message\n }\n\n if (options.nameFieldLabel) {\n dialog.nameFieldLabel = options.nameFieldLabel\n }\n\n if (options.showsTagField) {\n dialog.showsTagField = options.showsTagField\n }\n\n return {\n document: document,\n options: options,\n dialog: dialog,\n }\n}\n\n// https://github.com/electron/electron/blob/master/docs/api/dialog.md#dialogshowsavedialogbrowserwindow-options\nmodule.exports.saveDialog = function saveDialog(document, options) {\n var setup = setupOptions(document, options)\n\n return utils.runDialog(\n setup.dialog,\n function getResult(_dialog, returnCode) {\n return {\n canceled: returnCode != NSOKButton,\n filePath:\n returnCode == NSOKButton ? String(_dialog.URL().path()) : undefined,\n }\n },\n setup.document\n )\n}\n\n// https://github.com/electron/electron/blob/master/docs/api/dialog.md#dialogshowsavedialogsyncbrowserwindow-options\nmodule.exports.saveDialogSync = function saveDialogSync(document, options) {\n var setup = setupOptions(document, options)\n\n return utils.runDialogSync(\n setup.dialog,\n function getResult(_dialog, returnCode) {\n return returnCode == NSOKButton ? String(_dialog.URL().path()) : undefined\n },\n setup.document\n )\n}\n","module.exports.getURL = function getURL(path) {\n return NSURL.URLWithString(\n String(\n NSString.stringWithString(path).stringByExpandingTildeInPath()\n ).replace(/ /g, '%20')\n )\n}\n\nmodule.exports.runDialog = function runDialog(dialog, getResult, document) {\n if (!document) {\n var returnCode = dialog.runModal()\n return Promise.resolve(getResult(dialog, returnCode))\n }\n\n var fiber = coscript.createFiber()\n\n var window = (document.sketchObject || document).documentWindow()\n\n return new Promise(function p(resolve, reject) {\n dialog.beginSheetModalForWindow_completionHandler(\n window,\n __mocha__.createBlock_function('v16@?0q8', function onCompletion(\n _returnCode\n ) {\n try {\n resolve(getResult(dialog, _returnCode))\n } catch (err) {\n reject(err)\n }\n NSApp.endSheet(dialog)\n if (fiber) {\n fiber.cleanup()\n } else {\n coscript.shouldKeepAround = false\n }\n })\n )\n })\n}\n\nmodule.exports.runDialogSync = function runDialog(dialog, getResult, document) {\n var returnCode\n\n if (!document) {\n returnCode = dialog.runModal()\n return getResult(dialog, returnCode)\n }\n\n var window = (document.sketchObject || document).documentWindow()\n\n dialog.beginSheetModalForWindow_completionHandler(\n window,\n __mocha__.createBlock_function('v16@?0q8', function onCompletion(\n _returnCode\n ) {\n NSApp.stopModalWithCode(_returnCode)\n })\n )\n\n returnCode = NSApp.runModalForWindow(window)\n NSApp.endSheet(dialog)\n return getResult(dialog, returnCode)\n}\n","// TODO: async. Should probably be done with NSFileHandle and some notifications\n// TODO: file descriptor. Needs to be done with NSFileHandle\n\nmodule.exports.constants = {\n F_OK: 0,\n R_OK: 4,\n W_OK: 2,\n X_OK: 1\n}\n\nmodule.exports.accessSync = function(path, mode) {\n mode = mode | 0\n var fileManager = NSFileManager.defaultManager()\n\n switch (mode) {\n case 0:\n return module.exports.existsSync(path)\n case 1:\n return Boolean(fileManager.isExecutableFileAtPath(path))\n case 2:\n return Boolean(fileManager.isWritableFileAtPath(path))\n case 3:\n return Boolean(fileManager.isExecutableFileAtPath(path) && fileManager.isWritableFileAtPath(path))\n case 4:\n return Boolean(fileManager.isReadableFileAtPath(path))\n case 5:\n return Boolean(fileManager.isReadableFileAtPath(path) && fileManager.isExecutableFileAtPath(path))\n case 6:\n return Boolean(fileManager.isReadableFileAtPath(path) && fileManager.isWritableFileAtPath(path))\n case 7:\n return Boolean(fileManager.isReadableFileAtPath(path) && fileManager.isWritableFileAtPath(path) && fileManager.isExecutableFileAtPath(path))\n }\n}\n\nmodule.exports.appendFileSync = function(file, data, options) {\n if (!module.exports.existsSync(file)) {\n return module.exports.writeFileSync(file, data, options)\n }\n\n var handle = NSFileHandle.fileHandleForWritingAtPath(file)\n handle.seekToEndOfFile()\n\n if (data && data.mocha && data.mocha().class() === 'NSData') {\n handle.writeData(data)\n return\n }\n\n var encoding = options && options.encoding ? options.encoding : (options ? options : 'utf8')\n\n var string = NSString.stringWithString(data)\n var nsdata\n\n switch (encoding) {\n case 'utf8':\n nsdata = string.dataUsingEncoding(NSUTF8StringEncoding)\n break\n case 'ascii':\n nsdata = string.dataUsingEncoding(NSASCIIStringEncoding)\n break\n case 'utf16le':\n case 'ucs2':\n nsdata = string.dataUsingEncoding(NSUTF16LittleEndianStringEncoding)\n break\n case 'base64':\n var plainData = string.dataUsingEncoding(NSUTF8StringEncoding)\n nsdata = plainData.base64EncodedStringWithOptions(0).dataUsingEncoding(NSUTF8StringEncoding)\n break\n case 'latin1':\n case 'binary':\n nsdata = string.dataUsingEncoding(NSISOLatin1StringEncoding)\n break\n case 'hex':\n // TODO: how?\n default:\n nsdata = string.dataUsingEncoding(NSUTF8StringEncoding)\n break\n }\n\n handle.writeData(data)\n}\n\nmodule.exports.chmodSync = function(path, mode) {\n var err = MOPointer.alloc().init()\n var fileManager = NSFileManager.defaultManager()\n fileManager.setAttributes_ofItemAtPath_error({\n NSFilePosixPermissions: mode\n }, path, err)\n\n if (err.value() !== null) {\n throw new Error(err.value())\n }\n}\n\nmodule.exports.copyFileSync = function(path, dest, flags) {\n var err = MOPointer.alloc().init()\n var fileManager = NSFileManager.defaultManager()\n fileManager.copyItemAtPath_toPath_error(path, dest, err)\n\n if (err.value() !== null) {\n throw new Error(err.value())\n }\n}\n\nmodule.exports.existsSync = function(path) {\n var fileManager = NSFileManager.defaultManager()\n return Boolean(fileManager.fileExistsAtPath(path))\n}\n\nmodule.exports.linkSync = function(existingPath, newPath) {\n var err = MOPointer.alloc().init()\n var fileManager = NSFileManager.defaultManager()\n fileManager.linkItemAtPath_toPath_error(existingPath, newPath, err)\n\n if (err.value() !== null) {\n throw new Error(err.value())\n }\n}\n\nmodule.exports.mkdirSync = function(path, mode) {\n mode = mode || 0o777\n var err = MOPointer.alloc().init()\n var fileManager = NSFileManager.defaultManager()\n fileManager.createDirectoryAtPath_withIntermediateDirectories_attributes_error(path, false, {\n NSFilePosixPermissions: mode\n }, err)\n\n if (err.value() !== null) {\n throw new Error(err.value())\n }\n}\n\nmodule.exports.mkdtempSync = function(path) {\n function makeid() {\n var text = \"\";\n var possible = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\";\n\n for (var i = 0; i < 6; i++)\n text += possible.charAt(Math.floor(Math.random() * possible.length));\n\n return text;\n }\n var tempPath = path + makeid()\n module.exports.mkdirSync(tempPath)\n return tempPath\n}\n\nmodule.exports.readdirSync = function(path) {\n var fileManager = NSFileManager.defaultManager()\n var paths = fileManager.subpathsAtPath(path)\n var arr = []\n for (var i = 0; i < paths.length; i++) {\n arr.push(paths[i])\n }\n return arr\n}\n\nmodule.exports.readFileSync = function(path, options) {\n var encoding = options && options.encoding ? options.encoding : (options ? options : 'buffer')\n var fileManager = NSFileManager.defaultManager()\n var data = fileManager.contentsAtPath(path)\n switch (encoding) {\n case 'utf8':\n return String(NSString.alloc().initWithData_encoding(data, NSUTF8StringEncoding))\n case 'ascii':\n return String(NSString.alloc().initWithData_encoding(data, NSASCIIStringEncoding))\n case 'utf16le':\n case 'ucs2':\n return String(NSString.alloc().initWithData_encoding(data, NSUTF16LittleEndianStringEncoding))\n case 'base64':\n var nsdataDecoded = NSData.alloc().initWithBase64EncodedData_options(data, 0)\n return String(NSString.alloc().initWithData_encoding(nsdataDecoded, NSUTF8StringEncoding))\n case 'latin1':\n case 'binary':\n return String(NSString.alloc().initWithData_encoding(data, NSISOLatin1StringEncoding))\n case 'hex':\n // TODO: how?\n return data\n default:\n return data\n }\n}\n\nmodule.exports.readlinkSync = function(path) {\n var err = MOPointer.alloc().init()\n var fileManager = NSFileManager.defaultManager()\n var result = fileManager.destinationOfSymbolicLinkAtPath_error(path, err)\n\n if (err.value() !== null) {\n throw new Error(err.value())\n }\n\n return result\n}\n\nmodule.exports.realpathSync = function(path) {\n return NSString.stringByResolvingSymlinksInPath(path)\n}\n\nmodule.exports.renameSync = function(oldPath, newPath) {\n var err = MOPointer.alloc().init()\n var fileManager = NSFileManager.defaultManager()\n fileManager.moveItemAtPath_toPath_error(oldPath, newPath, err)\n\n if (err.value() !== null) {\n throw new Error(err.value())\n }\n}\n\nmodule.exports.rmdirSync = function(path) {\n var err = MOPointer.alloc().init()\n var fileManager = NSFileManager.defaultManager()\n fileManager.removeItemAtPath_error(path, err)\n\n if (err.value() !== null) {\n throw new Error(err.value())\n }\n}\n\nmodule.exports.statSync = function(path) {\n var err = MOPointer.alloc().init()\n var fileManager = NSFileManager.defaultManager()\n var result = fileManager.attributesOfItemAtPath_error(path, err)\n\n if (err.value() !== null) {\n throw new Error(err.value())\n }\n\n return {\n dev: String(result.NSFileDeviceIdentifier),\n // ino: 48064969, The file system specific \"Inode\" number for the file.\n mode: result.NSFileType | result.NSFilePosixPermissions,\n nlink: Number(result.NSFileReferenceCount),\n uid: String(result.NSFileOwnerAccountID),\n gid: String(result.NSFileGroupOwnerAccountID),\n // rdev: 0, A numeric device identifier if the file is considered \"special\".\n size: Number(result.NSFileSize),\n // blksize: 4096, The file system block size for i/o operations.\n // blocks: 8, The number of blocks allocated for this file.\n atimeMs: Number(result.NSFileModificationDate.timeIntervalSince1970()) * 1000,\n mtimeMs: Number(result.NSFileModificationDate.timeIntervalSince1970()) * 1000,\n ctimeMs: Number(result.NSFileModificationDate.timeIntervalSince1970()) * 1000,\n birthtimeMs: Number(result.NSFileCreationDate.timeIntervalSince1970()) * 1000,\n atime: new Date(Number(result.NSFileModificationDate.timeIntervalSince1970()) * 1000 + 0.5), // the 0.5 comes from the node source. Not sure why it's added but in doubt...\n mtime: new Date(Number(result.NSFileModificationDate.timeIntervalSince1970()) * 1000 + 0.5),\n ctime: new Date(Number(result.NSFileModificationDate.timeIntervalSince1970()) * 1000 + 0.5),\n birthtime: new Date(Number(result.NSFileCreationDate.timeIntervalSince1970()) * 1000 + 0.5),\n isBlockDevice: function() { return result.NSFileType === NSFileTypeBlockSpecial },\n isCharacterDevice: function() { return result.NSFileType === NSFileTypeCharacterSpecial },\n isDirectory: function() { return result.NSFileType === NSFileTypeDirectory },\n isFIFO: function() { return false },\n isFile: function() { return result.NSFileType === NSFileTypeRegular },\n isSocket: function() { return result.NSFileType === NSFileTypeSocket },\n isSymbolicLink: function() { return result.NSFileType === NSFileTypeSymbolicLink },\n }\n}\n\nmodule.exports.symlinkSync = function(target, path) {\n var err = MOPointer.alloc().init()\n var fileManager = NSFileManager.defaultManager()\n var result = fileManager.createSymbolicLinkAtPath_withDestinationPath_error(path, target, err)\n\n if (err.value() !== null) {\n throw new Error(err.value())\n }\n}\n\nmodule.exports.truncateSync = function(path, len) {\n var hFile = NSFileHandle.fileHandleForUpdatingAtPath(sFilePath)\n hFile.truncateFileAtOffset(len || 0)\n hFile.closeFile()\n}\n\nmodule.exports.unlinkSync = function(path) {\n var err = MOPointer.alloc().init()\n var fileManager = NSFileManager.defaultManager()\n var result = fileManager.removeItemAtPath_error(path, err)\n\n if (err.value() !== null) {\n throw new Error(err.value())\n }\n}\n\nmodule.exports.utimesSync = function(path, aTime, mTime) {\n var err = MOPointer.alloc().init()\n var fileManager = NSFileManager.defaultManager()\n var result = fileManager.setAttributes_ofItemAtPath_error({\n NSFileModificationDate: aTime\n }, path, err)\n\n if (err.value() !== null) {\n throw new Error(err.value())\n }\n}\n\nmodule.exports.writeFileSync = function(path, data, options) {\n var encoding = options && options.encoding ? options.encoding : (options ? options : 'utf8')\n\n if (data && data.mocha && data.mocha().class() === 'NSData') {\n data.writeToFile_atomically(path, true)\n return\n }\n\n var err = MOPointer.alloc().init()\n var string = NSString.stringWithString(data)\n\n switch (encoding) {\n case 'utf8':\n string.writeToFile_atomically_encoding_error(path, true, NSUTF8StringEncoding, err)\n break\n case 'ascii':\n string.writeToFile_atomically_encoding_error(path, true, NSASCIIStringEncoding, err)\n break\n case 'utf16le':\n case 'ucs2':\n string.writeToFile_atomically_encoding_error(path, true, NSUTF16LittleEndianStringEncoding, err)\n break\n case 'base64':\n var plainData = string.dataUsingEncoding(NSUTF8StringEncoding)\n var nsdataEncoded = plainData.base64EncodedStringWithOptions(0)\n nsdataEncoded.writeToFile_atomically(path, true)\n break\n case 'latin1':\n case 'binary':\n string.writeToFile_atomically_encoding_error(path, true, NSISOLatin1StringEncoding, err)\n break\n case 'hex':\n // TODO: how?\n default:\n string.writeToFile_atomically_encoding_error(path, true, NSUTF8StringEncoding, err)\n break\n }\n\n if (err.value() !== null) {\n throw new Error(err.value())\n }\n}\n","// Copyright Joyent, Inc. and other Node contributors.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a\n// copy of this software and associated documentation files (the\n// \"Software\"), to deal in the Software without restriction, including\n// without limitation the rights to use, copy, modify, merge, publish,\n// distribute, sublicense, and/or sell copies of the Software, and to permit\n// persons to whom the Software is furnished to do so, subject to the\n// following conditions:\n//\n// The above copyright notice and this permission notice shall be included\n// in all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS\n// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN\n// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,\n// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR\n// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE\n// USE OR OTHER DEALINGS IN THE SOFTWARE.\n\nvar sketchSpecifics = require('./sketch-specifics')\n\n// we only expose the posix implementation since Sketch only runs on macOS\n\nvar CHAR_FORWARD_SLASH = 47\nvar CHAR_DOT = 46\n\n// Resolves . and .. elements in a path with directory names\nfunction normalizeString(path, allowAboveRoot) {\n var res = ''\n var lastSegmentLength = 0\n var lastSlash = -1\n var dots = 0\n var code\n for (var i = 0; i <= path.length; i += 1) {\n if (i < path.length) code = path.charCodeAt(i)\n else if (code === CHAR_FORWARD_SLASH) break\n else code = CHAR_FORWARD_SLASH\n if (code === CHAR_FORWARD_SLASH) {\n if (lastSlash === i - 1 || dots === 1) {\n // NOOP\n } else if (lastSlash !== i - 1 && dots === 2) {\n if (\n res.length < 2 ||\n lastSegmentLength !== 2 ||\n res.charCodeAt(res.length - 1) !== CHAR_DOT ||\n res.charCodeAt(res.length - 2) !== CHAR_DOT\n ) {\n if (res.length > 2) {\n var lastSlashIndex = res.lastIndexOf('/')\n if (lastSlashIndex !== res.length - 1) {\n if (lastSlashIndex === -1) {\n res = ''\n lastSegmentLength = 0\n } else {\n res = res.slice(0, lastSlashIndex)\n lastSegmentLength = res.length - 1 - res.lastIndexOf('/')\n }\n lastSlash = i\n dots = 0\n continue\n }\n } else if (res.length === 2 || res.length === 1) {\n res = ''\n lastSegmentLength = 0\n lastSlash = i\n dots = 0\n continue\n }\n }\n if (allowAboveRoot) {\n if (res.length > 0) res += '/..'\n else res = '..'\n lastSegmentLength = 2\n }\n } else {\n if (res.length > 0) res += '/' + path.slice(lastSlash + 1, i)\n else res = path.slice(lastSlash + 1, i)\n lastSegmentLength = i - lastSlash - 1\n }\n lastSlash = i\n dots = 0\n } else if (code === CHAR_DOT && dots !== -1) {\n ++dots\n } else {\n dots = -1\n }\n }\n return res\n}\n\nfunction _format(sep, pathObject) {\n var dir = pathObject.dir || pathObject.root\n var base =\n pathObject.base || (pathObject.name || '') + (pathObject.ext || '')\n if (!dir) {\n return base\n }\n if (dir === pathObject.root) {\n return dir + base\n }\n return dir + sep + base\n}\n\nvar posix = {\n // path.resolve([from ...], to)\n resolve: function resolve() {\n var resolvedPath = ''\n var resolvedAbsolute = false\n var cwd\n\n for (var i = arguments.length - 1; i >= -1 && !resolvedAbsolute; i -= 1) {\n var path\n if (i >= 0) {\n path = arguments[i]\n } else {\n if (cwd === undefined) {\n cwd = posix.dirname(sketchSpecifics.cwd())\n }\n path = cwd\n }\n\n path = sketchSpecifics.getString(path, 'path')\n\n // Skip empty entries\n if (path.length === 0) {\n continue\n }\n\n resolvedPath = path + '/' + resolvedPath\n resolvedAbsolute = path.charCodeAt(0) === CHAR_FORWARD_SLASH\n }\n\n // At this point the path should be resolved to a full absolute path, but\n // handle relative paths to be safe (might happen when process.cwd() fails)\n\n // Normalize the path\n resolvedPath = normalizeString(resolvedPath, !resolvedAbsolute)\n\n if (resolvedAbsolute) {\n if (resolvedPath.length > 0) return '/' + resolvedPath\n else return '/'\n } else if (resolvedPath.length > 0) {\n return resolvedPath\n } else {\n return '.'\n }\n },\n\n normalize: function normalize(path) {\n path = sketchSpecifics.getString(path, 'path')\n\n if (path.length === 0) return '.'\n\n var isAbsolute = path.charCodeAt(0) === CHAR_FORWARD_SLASH\n var trailingSeparator =\n path.charCodeAt(path.length - 1) === CHAR_FORWARD_SLASH\n\n // Normalize the path\n path = normalizeString(path, !isAbsolute)\n\n if (path.length === 0 && !isAbsolute) path = '.'\n if (path.length > 0 && trailingSeparator) path += '/'\n\n if (isAbsolute) return '/' + path\n return path\n },\n\n isAbsolute: function isAbsolute(path) {\n path = sketchSpecifics.getString(path, 'path')\n return path.length > 0 && path.charCodeAt(0) === CHAR_FORWARD_SLASH\n },\n\n join: function join() {\n if (arguments.length === 0) return '.'\n var joined\n for (var i = 0; i < arguments.length; i += 1) {\n var arg = arguments[i]\n arg = sketchSpecifics.getString(arg, 'path')\n if (arg.length > 0) {\n if (joined === undefined) joined = arg\n else joined += '/' + arg\n }\n }\n if (joined === undefined) return '.'\n return posix.normalize(joined)\n },\n\n relative: function relative(from, to) {\n from = sketchSpecifics.getString(from, 'from path')\n to = sketchSpecifics.getString(to, 'to path')\n\n if (from === to) return ''\n\n from = posix.resolve(from)\n to = posix.resolve(to)\n\n if (from === to) return ''\n\n // Trim any leading backslashes\n var fromStart = 1\n for (; fromStart < from.length; fromStart += 1) {\n if (from.charCodeAt(fromStart) !== CHAR_FORWARD_SLASH) break\n }\n var fromEnd = from.length\n var fromLen = fromEnd - fromStart\n\n // Trim any leading backslashes\n var toStart = 1\n for (; toStart < to.length; toStart += 1) {\n if (to.charCodeAt(toStart) !== CHAR_FORWARD_SLASH) break\n }\n var toEnd = to.length\n var toLen = toEnd - toStart\n\n // Compare paths to find the longest common path from root\n var length = fromLen < toLen ? fromLen : toLen\n var lastCommonSep = -1\n var i = 0\n for (; i <= length; i += 1) {\n if (i === length) {\n if (toLen > length) {\n if (to.charCodeAt(toStart + i) === CHAR_FORWARD_SLASH) {\n // We get here if `from` is the exact base path for `to`.\n // For example: from='/foo/bar'; to='/foo/bar/baz'\n return to.slice(toStart + i + 1)\n } else if (i === 0) {\n // We get here if `from` is the root\n // For example: from='/'; to='/foo'\n return to.slice(toStart + i)\n }\n } else if (fromLen > length) {\n if (from.charCodeAt(fromStart + i) === CHAR_FORWARD_SLASH) {\n // We get here if `to` is the exact base path for `from`.\n // For example: from='/foo/bar/baz'; to='/foo/bar'\n lastCommonSep = i\n } else if (i === 0) {\n // We get here if `to` is the root.\n // For example: from='/foo'; to='/'\n lastCommonSep = 0\n }\n }\n break\n }\n var fromCode = from.charCodeAt(fromStart + i)\n var toCode = to.charCodeAt(toStart + i)\n if (fromCode !== toCode) break\n else if (fromCode === CHAR_FORWARD_SLASH) lastCommonSep = i\n }\n\n var out = ''\n // Generate the relative path based on the path difference between `to`\n // and `from`\n for (i = fromStart + lastCommonSep + 1; i <= fromEnd; i += 1) {\n if (i === fromEnd || from.charCodeAt(i) === CHAR_FORWARD_SLASH) {\n if (out.length === 0) out += '..'\n else out += '/..'\n }\n }\n\n // Lastly, append the rest of the destination (`to`) path that comes after\n // the common path parts\n if (out.length > 0) return out + to.slice(toStart + lastCommonSep)\n else {\n toStart += lastCommonSep\n if (to.charCodeAt(toStart) === CHAR_FORWARD_SLASH) toStart += 1\n return to.slice(toStart)\n }\n },\n\n toNamespacedPath: function toNamespacedPath(path) {\n // Non-op on posix systems\n return path\n },\n\n dirname: function dirname(path) {\n path = sketchSpecifics.getString(path, 'path')\n if (path.length === 0) return '.'\n var code = path.charCodeAt(0)\n var hasRoot = code === CHAR_FORWARD_SLASH\n var end = -1\n var matchedSlash = true\n for (var i = path.length - 1; i >= 1; i -= 1) {\n code = path.charCodeAt(i)\n if (code === CHAR_FORWARD_SLASH) {\n if (!matchedSlash) {\n end = i\n break\n }\n } else {\n // We saw the first non-path separator\n matchedSlash = false\n }\n }\n\n if (end === -1) return hasRoot ? '/' : '.'\n if (hasRoot && end === 1) return '//'\n return path.slice(0, end)\n },\n\n basename: function basename(path, ext) {\n if (ext !== undefined)\n ext = sketchSpecifics.getString(ext, 'ext')\n path = sketchSpecifics.getString(path, 'path')\n\n var start = 0\n var end = -1\n var matchedSlash = true\n var i\n\n if (ext !== undefined && ext.length > 0 && ext.length <= path.length) {\n if (ext.length === path.length && ext === path) return ''\n var extIdx = ext.length - 1\n var firstNonSlashEnd = -1\n for (i = path.length - 1; i >= 0; i -= 1) {\n var code = path.charCodeAt(i)\n if (code === CHAR_FORWARD_SLASH) {\n // If we reached a path separator that was not part of a set of path\n // separators at the end of the string, stop now\n if (!matchedSlash) {\n start = i + 1\n break\n }\n } else {\n if (firstNonSlashEnd === -1) {\n // We saw the first non-path separator, remember this index in case\n // we need it if the extension ends up not matching\n matchedSlash = false\n firstNonSlashEnd = i + 1\n }\n if (extIdx >= 0) {\n // Try to match the explicit extension\n if (code === ext.charCodeAt(extIdx)) {\n if (--extIdx === -1) {\n // We matched the extension, so mark this as the end of our path\n // component\n end = i\n }\n } else {\n // Extension does not match, so our result is the entire path\n // component\n extIdx = -1\n end = firstNonSlashEnd\n }\n }\n }\n }\n\n if (start === end) end = firstNonSlashEnd\n else if (end === -1) end = path.length\n return path.slice(start, end)\n } else {\n for (i = path.length - 1; i >= 0; --i) {\n if (path.charCodeAt(i) === CHAR_FORWARD_SLASH) {\n // If we reached a path separator that was not part of a set of path\n // separators at the end of the string, stop now\n if (!matchedSlash) {\n start = i + 1\n break\n }\n } else if (end === -1) {\n // We saw the first non-path separator, mark this as the end of our\n // path component\n matchedSlash = false\n end = i + 1\n }\n }\n\n if (end === -1) return ''\n return path.slice(start, end)\n }\n },\n\n extname: function extname(path) {\n path = sketchSpecifics.getString(path, 'path')\n var startDot = -1\n var startPart = 0\n var end = -1\n var matchedSlash = true\n // Track the state of characters (if any) we see before our first dot and\n // after any path separator we find\n var preDotState = 0\n for (var i = path.length - 1; i >= 0; --i) {\n var code = path.charCodeAt(i)\n if (code === CHAR_FORWARD_SLASH) {\n // If we reached a path separator that was not part of a set of path\n // separators at the end of the string, stop now\n if (!matchedSlash) {\n startPart = i + 1\n break\n }\n continue\n }\n if (end === -1) {\n // We saw the first non-path separator, mark this as the end of our\n // extension\n matchedSlash = false\n end = i + 1\n }\n if (code === CHAR_DOT) {\n // If this is our first dot, mark it as the start of our extension\n if (startDot === -1) startDot = i\n else if (preDotState !== 1) preDotState = 1\n } else if (startDot !== -1) {\n // We saw a non-dot and non-path separator before our dot, so we should\n // have a good chance at having a non-empty extension\n preDotState = -1\n }\n }\n\n if (\n startDot === -1 ||\n end === -1 ||\n // We saw a non-dot character immediately before the dot\n preDotState === 0 ||\n // The (right-most) trimmed path component is exactly '..'\n (preDotState === 1 && startDot === end - 1 && startDot === startPart + 1)\n ) {\n return ''\n }\n return path.slice(startDot, end)\n },\n\n format: function format(pathObject) {\n if (pathObject === null || typeof pathObject !== 'object') {\n throw new Error('pathObject should be an Object')\n }\n return _format('/', pathObject)\n },\n\n parse: function parse(path) {\n path = sketchSpecifics.getString(path, 'path')\n\n var ret = { root: '', dir: '', base: '', ext: '', name: '' }\n if (path.length === 0) return ret\n var code = path.charCodeAt(0)\n var isAbsolute = code === CHAR_FORWARD_SLASH\n var start\n if (isAbsolute) {\n ret.root = '/'\n start = 1\n } else {\n start = 0\n }\n var startDot = -1\n var startPart = 0\n var end = -1\n var matchedSlash = true\n var i = path.length - 1\n\n // Track the state of characters (if any) we see before our first dot and\n // after any path separator we find\n var preDotState = 0\n\n // Get non-dir info\n for (; i >= start; --i) {\n code = path.charCodeAt(i)\n if (code === CHAR_FORWARD_SLASH) {\n // If we reached a path separator that was not part of a set of path\n // separators at the end of the string, stop now\n if (!matchedSlash) {\n startPart = i + 1\n break\n }\n continue\n }\n if (end === -1) {\n // We saw the first non-path separator, mark this as the end of our\n // extension\n matchedSlash = false\n end = i + 1\n }\n if (code === CHAR_DOT) {\n // If this is our first dot, mark it as the start of our extension\n if (startDot === -1) startDot = i\n else if (preDotState !== 1) preDotState = 1\n } else if (startDot !== -1) {\n // We saw a non-dot and non-path separator before our dot, so we should\n // have a good chance at having a non-empty extension\n preDotState = -1\n }\n }\n\n if (\n startDot === -1 ||\n end === -1 ||\n // We saw a non-dot character immediately before the dot\n preDotState === 0 ||\n // The (right-most) trimmed path component is exactly '..'\n (preDotState === 1 && startDot === end - 1 && startDot === startPart + 1)\n ) {\n if (end !== -1) {\n if (startPart === 0 && isAbsolute)\n ret.base = ret.name = path.slice(1, end)\n else ret.base = ret.name = path.slice(startPart, end)\n }\n } else {\n if (startPart === 0 && isAbsolute) {\n ret.name = path.slice(1, startDot)\n ret.base = path.slice(1, end)\n } else {\n ret.name = path.slice(startPart, startDot)\n ret.base = path.slice(startPart, end)\n }\n ret.ext = path.slice(startDot, end)\n }\n\n if (startPart > 0) ret.dir = path.slice(0, startPart - 1)\n else if (isAbsolute) ret.dir = '/'\n\n return ret\n },\n\n sep: '/',\n delimiter: ':',\n win32: null,\n posix: null,\n\n resourcePath: sketchSpecifics.resourcePath,\n}\n\nmodule.exports = posix\nmodule.exports.posix = posix\n","var util = require('util')\n\nmodule.exports.getString = function getString(path, argumentName) {\n if (!util.isString(path)) {\n // let's make a special case for NSURL\n if (util.getNativeClass(path) === 'NSURL') {\n return String(path.path().copy())\n }\n throw new Error(argumentName + ' should be a string. Got ' + typeof path + ' instead.')\n }\n return String(path)\n}\n\nmodule.exports.cwd = function cwd() {\n if (typeof __command !== 'undefined' && __command.script() && __command.script().URL()) {\n return String(__command.script().URL().path().copy())\n }\n return String(MSPluginManager.defaultPluginURL().path().copy())\n}\n\nmodule.exports.resourcePath = function resourcePath(resourceName) {\n if (typeof __command === 'undefined' || !__command.pluginBundle()) {\n return undefined\n }\n var resource = __command.pluginBundle().urlForResourceNamed(resourceName)\n if (!resource) {\n return undefined\n }\n return String(resource.path())\n}\n","/* from https://github.com/taylorhakes/promise-polyfill */\n\nfunction promiseFinally(callback) {\n var constructor = this.constructor;\n return this.then(\n function(value) {\n return constructor.resolve(callback()).then(function() {\n return value;\n });\n },\n function(reason) {\n return constructor.resolve(callback()).then(function() {\n return constructor.reject(reason);\n });\n }\n );\n}\n\nfunction noop() {}\n\n/**\n * @constructor\n * @param {Function} fn\n */\nfunction Promise(fn) {\n if (!(this instanceof Promise))\n throw new TypeError(\"Promises must be constructed via new\");\n if (typeof fn !== \"function\") throw new TypeError(\"not a function\");\n /** @type {!number} */\n this._state = 0;\n /** @type {!boolean} */\n this._handled = false;\n /** @type {Promise|undefined} */\n this._value = undefined;\n /** @type {!Array} */\n this._deferreds = [];\n\n doResolve(fn, this);\n}\n\nfunction handle(self, deferred) {\n while (self._state === 3) {\n self = self._value;\n }\n if (self._state === 0) {\n self._deferreds.push(deferred);\n return;\n }\n self._handled = true;\n Promise._immediateFn(function() {\n var cb = self._state === 1 ? deferred.onFulfilled : deferred.onRejected;\n if (cb === null) {\n (self._state === 1 ? resolve : reject)(deferred.promise, self._value);\n return;\n }\n var ret;\n try {\n ret = cb(self._value);\n } catch (e) {\n reject(deferred.promise, e);\n return;\n }\n resolve(deferred.promise, ret);\n });\n}\n\nfunction resolve(self, newValue) {\n try {\n // Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure\n if (newValue === self)\n throw new TypeError(\"A promise cannot be resolved with itself.\");\n if (\n newValue &&\n (typeof newValue === \"object\" || typeof newValue === \"function\")\n ) {\n var then = newValue.then;\n if (newValue instanceof Promise) {\n self._state = 3;\n self._value = newValue;\n finale(self);\n return;\n } else if (typeof then === \"function\") {\n doResolve(then.bind(newValue), self);\n return;\n }\n }\n self._state = 1;\n self._value = newValue;\n finale(self);\n } catch (e) {\n reject(self, e);\n }\n}\n\nfunction reject(self, newValue) {\n self._state = 2;\n self._value = newValue;\n finale(self);\n}\n\nfunction finale(self) {\n if (self._state === 2 && self._deferreds.length === 0) {\n Promise._immediateFn(function() {\n if (!self._handled) {\n Promise._unhandledRejectionFn(self._value, self);\n }\n });\n }\n\n for (var i = 0, len = self._deferreds.length; i < len; i++) {\n handle(self, self._deferreds[i]);\n }\n self._deferreds = null;\n}\n\n/**\n * @constructor\n */\nfunction Handler(onFulfilled, onRejected, promise) {\n this.onFulfilled = typeof onFulfilled === \"function\" ? onFulfilled : null;\n this.onRejected = typeof onRejected === \"function\" ? onRejected : null;\n this.promise = promise;\n}\n\n/**\n * Take a potentially misbehaving resolver function and make sure\n * onFulfilled and onRejected are only called once.\n *\n * Makes no guarantees about asynchrony.\n */\nfunction doResolve(fn, self) {\n var done = false;\n try {\n fn(\n function(value) {\n if (done) {\n Promise._multipleResolvesFn(\"resolve\", self, value);\n return;\n }\n done = true;\n resolve(self, value);\n },\n function(reason) {\n if (done) {\n Promise._multipleResolvesFn(\"reject\", self, reason);\n return;\n }\n done = true;\n reject(self, reason);\n }\n );\n } catch (ex) {\n if (done) {\n Promise._multipleResolvesFn(\"reject\", self, ex);\n return;\n }\n done = true;\n reject(self, ex);\n }\n}\n\nPromise.prototype[\"catch\"] = function(onRejected) {\n return this.then(null, onRejected);\n};\n\nPromise.prototype.then = function(onFulfilled, onRejected) {\n // @ts-ignore\n var prom = new this.constructor(noop);\n\n handle(this, new Handler(onFulfilled, onRejected, prom));\n return prom;\n};\n\nPromise.prototype[\"finally\"] = promiseFinally;\n\nPromise.all = function(arr) {\n return new Promise(function(resolve, reject) {\n if (!Array.isArray(arr)) {\n return reject(new TypeError(\"Promise.all accepts an array\"));\n }\n\n var args = Array.prototype.slice.call(arr);\n if (args.length === 0) return resolve([]);\n var remaining = args.length;\n\n function res(i, val) {\n try {\n if (val && (typeof val === \"object\" || typeof val === \"function\")) {\n var then = val.then;\n if (typeof then === \"function\") {\n then.call(\n val,\n function(val) {\n res(i, val);\n },\n reject\n );\n return;\n }\n }\n args[i] = val;\n if (--remaining === 0) {\n resolve(args);\n }\n } catch (ex) {\n reject(ex);\n }\n }\n\n for (var i = 0; i < args.length; i++) {\n res(i, args[i]);\n }\n });\n};\n\nPromise.resolve = function(value) {\n if (value && typeof value === \"object\" && value.constructor === Promise) {\n return value;\n }\n\n return new Promise(function(resolve) {\n resolve(value);\n });\n};\n\nPromise.reject = function(value) {\n return new Promise(function(resolve, reject) {\n reject(value);\n });\n};\n\nPromise.race = function(arr) {\n return new Promise(function(resolve, reject) {\n if (!Array.isArray(arr)) {\n return reject(new TypeError(\"Promise.race accepts an array\"));\n }\n\n for (var i = 0, len = arr.length; i < len; i++) {\n Promise.resolve(arr[i]).then(resolve, reject);\n }\n });\n};\n\n// Use polyfill for setImmediate for performance gains\nPromise._immediateFn = setImmediate;\n\nPromise._unhandledRejectionFn = function _unhandledRejectionFn(err, promise) {\n if (\n typeof process !== \"undefined\" &&\n process.listenerCount &&\n (process.listenerCount(\"unhandledRejection\") ||\n process.listenerCount(\"uncaughtException\"))\n ) {\n process.emit(\"unhandledRejection\", err, promise);\n process.emit(\"uncaughtException\", err, \"unhandledRejection\");\n } else if (typeof console !== \"undefined\" && console) {\n console.warn(\"Possible Unhandled Promise Rejection:\", err);\n }\n};\n\nPromise._multipleResolvesFn = function _multipleResolvesFn(\n type,\n promise,\n value\n) {\n if (typeof process !== \"undefined\" && process.emit) {\n process.emit(\"multipleResolves\", type, promise, value);\n }\n};\n\nmodule.exports = Promise;\n","/*\n change for npm modules.\n by Luiz Estácio.\n\n json-format v.1.1\n http://github.com/phoboslab/json-format\n\n Released under MIT license:\n http://www.opensource.org/licenses/mit-license.php\n*/\nvar p = [],\n indentConfig = {\n tab: { char: '\\t', size: 1 },\n space: { char: ' ', size: 4 }\n },\n configDefault = {\n type: 'tab'\n },\n push = function( m ) { return '\\\\' + p.push( m ) + '\\\\'; },\n pop = function( m, i ) { return p[i-1] },\n tabs = function( count, indentType) { return new Array( count + 1 ).join( indentType ); };\n\nfunction JSONFormat ( json, indentType ) {\n p = [];\n var out = \"\",\n indent = 0;\n\n // Extract backslashes and strings\n json = json\n .replace( /\\\\./g, push )\n .replace( /(\".*?\"|'.*?')/g, push )\n .replace( /\\s+/, '' ); \n\n // Indent and insert newlines\n for( var i = 0; i < json.length; i++ ) {\n var c = json.charAt(i);\n\n switch(c) {\n case '{':\n case '[':\n out += c + \"\\n\" + tabs(++indent, indentType);\n break;\n case '}':\n case ']':\n out += \"\\n\" + tabs(--indent, indentType) + c;\n break;\n case ',':\n out += \",\\n\" + tabs(indent, indentType);\n break;\n case ':':\n out += \": \";\n break;\n default:\n out += c;\n break; \n } \n }\n\n // Strip whitespace from numeric arrays and put backslashes \n // and strings back in\n out = out\n .replace( /\\[[\\d,\\s]+?\\]/g, function(m){ return m.replace(/\\s/g,''); } )\n .replace( /\\\\(\\d+)\\\\/g, pop ) // strings\n .replace( /\\\\(\\d+)\\\\/g, pop ); // backslashes in strings\n\n return out;\n};\n\nmodule.exports = function(json, config){\n config = config || configDefault;\n var indent = indentConfig[config.type];\n\n if ( indent == null ) {\n throw new Error('Unrecognized indent type: \"' + config.type + '\"');\n }\n var indentType = new Array((config.size || indent.size) + 1).join(indent.char);\n return JSONFormat(JSON.stringify(json), indentType);\n}\n","\nimport dom from \"sketch/dom\";\nimport UI from \"sketch/ui\";\n\nfunction convertSketchColourToRGBA(colour) {\n const red = Math.round(colour.red() * 255);\n const green = Math.round(colour.green() * 255);\n const blue = Math.round(colour.blue() * 255);\n return \"rgba(\" + red + \",\" + green + \",\" + blue + \",\" + colour.alpha() + \")\";\n}\n\nexport function extractStyles(context, convert) {\n const doc = context.document;\n const pages = doc.pages();\n\n // we need to check if we have all the pages\n let pagesExist = {\n alignments: false,\n colors: false,\n styles: false\n };\n\n // arrays and objects for our styles\n let TypographyStyles = [];\n let DocumentColours = {};\n let textAlignments = [];\n\n pages.forEach(page => {\n // alignments\n if (String(page.name()) === \"Alignments\") {\n // page exists set to true\n pagesExist.alignments = true;\n\n page.layers().forEach(layer => {\n //log(layer.name() + ' ' + layer.textAlignment())\n let alignment = \"left\";\n if (layer.textAlignment() === 4) alignment = \"left\";\n if (layer.textAlignment() === 2) alignment = \"center\";\n if (layer.textAlignment() === 1) alignment = \"right\";\n textAlignments.push(alignment);\n });\n }\n\n // page styles\n if (String(page.name()) === \"Styles\") {\n // page exists set to true\n pagesExist.styles = true;\n\n // get styles\n page.layers().forEach(layer => {\n if (layer.class() === MSTextLayer) {\n // log(layer.font().fontName())\n // log(layer.fontSize())\n // log(layer.lineHeight())\n // log(layer.characterSpacing())\n // log(layer.style().textStyle().encodedAttributes() )\n //log(layer.styleAttributes()[\"MSAttributedStringTextTransformAttribute\"])\n let textTransform = \"none\";\n\n if (\n String(\n layer.styleAttributes()[\n \"MSAttributedStringTextTransformAttribute\"\n ]\n ) === \"1\"\n )\n textTransform = \"uppercase\"; // null: none, 1: uppercase and 2 lowercase\n if (\n String(\n layer.styleAttributes()[\n \"MSAttributedStringTextTransformAttribute\"\n ]\n ) === \"2\"\n )\n textTransform = \"lowercase\";\n\n // console.log( String(layer.name()) + \" \" + layer.font().fontName() + \" \" + dom.fromNative(layer).style.fontWeight + \" \" + dom.fromNative(layer).style.fontStyle )\n // console.log(\"-----\" )\n\n // fontFamily : dom.fromNative(layer).style.fontFamily ,\n // fontWeight : dom.fromNative(layer).style.fontWeight ,\n //console.log(dom.fromNative(layer).style.fontStyle)\n //console.log(dom.fromNative(layer).style.borders)\n\n \n TypographyStyles.push({\n name: String(layer.name()),\n styles: {\n fontFamily: dom.fromNative(layer).style.fontFamily,\n fontWeight: convert\n ? dom.fromNative(layer).style.fontWeight * 100\n : dom.fromNative(layer).style.fontWeight,\n fontSize: layer.fontSize() + (convert ? \"px\" : \"\"),\n lineHeight: layer.lineHeight() + (convert ? \"px\" : \"\"),\n fontStyle:\n dom.fromNative(layer).style.fontStyle != undefined\n ? dom.fromNative(layer).style.fontStyle\n : \"normal\",\n paragraphSpacing: dom.fromNative(layer).style.paragraphSpacing,\n ...(convert && {\n letterSpacing: String(layer.characterSpacing() / 10 + \"em\")\n }),\n ...(!convert && { kerning: layer.characterSpacing() }),\n textTransform: textTransform,\n borders: dom.fromNative(layer).style.borders || []\n },\n alignments: textAlignments,\n adjustments: []\n });\n }\n });\n }\n\n // get colours\n if (String(page.name()) === \"Colours\" || String(page.name()) === \"Colors\") {\n // page exists set to true\n pagesExist.colors = true;\n\n DocumentColours = {};\n page.layers().forEach(layer => {\n DocumentColours[layer.name()] = convert\n ? convertSketchColourToRGBA(\n layer\n .style()\n .firstEnabledFill()\n .color()\n )\n : layer\n .style()\n .firstEnabledFill()\n .color();\n });\n }\n });\n\n\n let allPagesHere = true;\n let messages = [];\n\n // first check for pages\n if (!pagesExist.alignments) {\n messages.push('Your document is missing the page named \"Alignments\".');\n allPagesHere = false;\n }\n\n if (!pagesExist.colors) {\n messages.push('Your document is missing the page named \"Colors\". ');\n allPagesHere = false;\n }\n\n if (!pagesExist.styles) {\n messages.push('Your document is missing the page named \"Styles\".');\n allPagesHere = false;\n }\n\n // now that we have checked for pages, lets see if there is anything in them\n if (pagesExist.alignments && textAlignments.length === 0) {\n messages.push(\"Your alignments page has no alignments in it.\");\n allPagesHere = false;\n }\n\n if (pagesExist.colors && Object.keys(DocumentColours).length === 0) {\n messages.push(\"Your colors page has no colors in it.\");\n allPagesHere = false;\n }\n\n if (pagesExist.styles && TypographyStyles.length === 0) {\n messages.push(\"Your styles page has no styles in it.\");\n allPagesHere = false;\n }\n\n let messageString = \"\";\n messages.forEach(message => {\n messageString += message + \"\\n\";\n });\n\n if (!allPagesHere) UI.alert(\"Unable to render styles\", messageString);\n\n const DesignSystemTokens = {\n colours: DocumentColours,\n typography: TypographyStyles,\n textAlignments: textAlignments,\n render: allPagesHere\n };\n\n return DesignSystemTokens;\n}\n\nexport function generateTextStyles(json) {\n // can we calculate this beforehand?\n let typeStyles = new Array();\n\n json.typography.forEach(item => {\n Object.keys(json.colours).forEach(colour => {\n item.alignments.map((align, index) => {\n // this splits at a slash and adds the adjustments for breakpoints after the alignment\n // assumption is that there is only one adjusment\n let name = item.name.split(\"/\");\n typeStyles.push({\n name: `${name[0]}/${colour}/${index +\n \"_\" +\n align +\n (name.length > 1 ? \"/\" + name[1] : \"\")}`,\n style: {\n textColor: dom.Style.colorToString(json.colours[colour]),\n alignment: align,\n ...item.styles\n }\n });\n });\n });\n });\n\n return typeStyles;\n}\n\nfunction checkMatch(baseStyle, newStyle, prop) {\n let value = true;\n\n // for now we are just going off the previous style. As this would need to check\n // every prop across every adjustment :/\n if (JSON.stringify(baseStyle[prop]) === JSON.stringify(newStyle[prop])) {\n // very primitive and breaks if order is out of sync\n value = false;\n }\n\n //log( prop + ' ' + baseStyle[prop] + ' ----- ' + prop + ' ' +newStyle[prop] + ' value ' + value)\n\n return value;\n}\n\nexport function generateJSONStyles(json, arrayFormat) {\n let typeStyles = {};\n let refinedBreakpoints = [];\n //log(json.typography)\n json.typography.forEach(item => {\n let name = item.name.split(\"/\");\n if (!typeStyles[name[0]]) {\n typeStyles[name[0]] = {\n name: name[0],\n styles: item.styles,\n alignments: item.alignments,\n adjustments: []\n };\n } else {\n let currentStyle = item.styles;\n //let previousStyle = typeStyles[name[0]].styles\n // work out previous style\n const adjustmentLength = typeStyles[name[0]].adjustments.length;\n refinedBreakpoints[adjustmentLength] = { name: name[1], styles: {} };\n\n // if(adjustmentLength>0){\n // previousStyle = typeStyles[name[0]].adjustments[adjustmentLength-1]\n // }\n\n //previousStyle,\n Object.keys(currentStyle).map(checked => {\n if (checkMatch(typeStyles[name[0]].styles, currentStyle, checked)) {\n refinedBreakpoints[adjustmentLength].styles[checked] =\n currentStyle[checked];\n }\n });\n\n typeStyles[name[0]].adjustments.push(\n refinedBreakpoints[adjustmentLength]\n );\n }\n });\n\n // finally merge colours back in and return text to an array\n let formattedTokens = {\n colours: json.colours,\n typography: arrayFormat\n ? Object.keys(typeStyles).map(function(key) {\n return typeStyles[key];\n })\n : typeStyles\n };\n return formattedTokens;\n}\n","import dialog from \"@skpm/dialog\";\nimport path from \"@skpm/path\";\nimport fs from \"@skpm/fs\";\nimport jsonFormat from \"json-format\";\nimport Sketch from \"sketch\";\n\nimport { extractStyles, generateJSONStyles } from \"./generators\";\n\nfunction save(filename, fileContents) {\n console.log(filename)\n\n const targetFile = path.resolve(filename);\n fs.writeFileSync(targetFile, fileContents, \"utf8\");\n}\n\nexport default function(context) {\n const doc = context.document;\n\n // ok lets get the styles\n\n const designTokens = extractStyles(context, true);\n\n if (designTokens.render) {\n const options = [\"Array\", \"Object\"];\n const textSaveSelection = Sketch.UI.getInputFromUser(\n \"Would you like the text styles as an Array or Object\",\n {\n type: Sketch.UI.INPUT_TYPE.selection,\n possibleValues: options\n },\n (err, value) => {\n if (err) {\n // most likely the user canceled the input\n return;\n } else {\n if (value[2]) {\n let textSaveMethod = true;\n if (value === \"Object\") textSaveMethod = false;\n if (value === \"Array\") textSaveMethod = true;\n const arranged = generateJSONStyles(designTokens, textSaveMethod);\n\n // Save the file\n let saveDialog = dialog.showSaveDialog(\n doc,\n {\n defaultPath: \"tokens.json\",\n message: \"Choose a folder to save your tokens\"\n }\n ).then( result => {\n if(!result.cancelled) save(result.filePath, jsonFormat(arranged));\n });\n\n\n }\n }\n }\n );\n } else {\n context.document.showMessage('No styles rendered. Check your document setup. Documentation here https://github.com/tbrasington/text-to-styles');\n }\n}\n","module.exports = require(\"sketch\");","module.exports = require(\"sketch/dom\");","module.exports = require(\"sketch/ui\");","module.exports = require(\"util\");"],"sourceRoot":""} -------------------------------------------------------------------------------- /text-to-styles.sketchplugin/Contents/Sketch/__renderStyleSheet.js: -------------------------------------------------------------------------------- 1 | var globalThis = this; 2 | var global = this; 3 | function __skpm_run (key, context) { 4 | globalThis.context = context; 5 | try { 6 | 7 | var exports = 8 | /******/ (function(modules) { // webpackBootstrap 9 | /******/ // The module cache 10 | /******/ var installedModules = {}; 11 | /******/ 12 | /******/ // The require function 13 | /******/ function __webpack_require__(moduleId) { 14 | /******/ 15 | /******/ // Check if module is in cache 16 | /******/ if(installedModules[moduleId]) { 17 | /******/ return installedModules[moduleId].exports; 18 | /******/ } 19 | /******/ // Create a new module (and put it into the cache) 20 | /******/ var module = installedModules[moduleId] = { 21 | /******/ i: moduleId, 22 | /******/ l: false, 23 | /******/ exports: {} 24 | /******/ }; 25 | /******/ 26 | /******/ // Execute the module function 27 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); 28 | /******/ 29 | /******/ // Flag the module as loaded 30 | /******/ module.l = true; 31 | /******/ 32 | /******/ // Return the exports of the module 33 | /******/ return module.exports; 34 | /******/ } 35 | /******/ 36 | /******/ 37 | /******/ // expose the modules object (__webpack_modules__) 38 | /******/ __webpack_require__.m = modules; 39 | /******/ 40 | /******/ // expose the module cache 41 | /******/ __webpack_require__.c = installedModules; 42 | /******/ 43 | /******/ // define getter function for harmony exports 44 | /******/ __webpack_require__.d = function(exports, name, getter) { 45 | /******/ if(!__webpack_require__.o(exports, name)) { 46 | /******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); 47 | /******/ } 48 | /******/ }; 49 | /******/ 50 | /******/ // define __esModule on exports 51 | /******/ __webpack_require__.r = function(exports) { 52 | /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { 53 | /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); 54 | /******/ } 55 | /******/ Object.defineProperty(exports, '__esModule', { value: true }); 56 | /******/ }; 57 | /******/ 58 | /******/ // create a fake namespace object 59 | /******/ // mode & 1: value is a module id, require it 60 | /******/ // mode & 2: merge all properties of value into the ns 61 | /******/ // mode & 4: return value when already ns object 62 | /******/ // mode & 8|1: behave like require 63 | /******/ __webpack_require__.t = function(value, mode) { 64 | /******/ if(mode & 1) value = __webpack_require__(value); 65 | /******/ if(mode & 8) return value; 66 | /******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; 67 | /******/ var ns = Object.create(null); 68 | /******/ __webpack_require__.r(ns); 69 | /******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value }); 70 | /******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); 71 | /******/ return ns; 72 | /******/ }; 73 | /******/ 74 | /******/ // getDefaultExport function for compatibility with non-harmony modules 75 | /******/ __webpack_require__.n = function(module) { 76 | /******/ var getter = module && module.__esModule ? 77 | /******/ function getDefault() { return module['default']; } : 78 | /******/ function getModuleExports() { return module; }; 79 | /******/ __webpack_require__.d(getter, 'a', getter); 80 | /******/ return getter; 81 | /******/ }; 82 | /******/ 83 | /******/ // Object.prototype.hasOwnProperty.call 84 | /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; 85 | /******/ 86 | /******/ // __webpack_public_path__ 87 | /******/ __webpack_require__.p = ""; 88 | /******/ 89 | /******/ 90 | /******/ // Load entry module and return exports 91 | /******/ return __webpack_require__(__webpack_require__.s = "./src/renderStyleSheet.js"); 92 | /******/ }) 93 | /************************************************************************/ 94 | /******/ ({ 95 | 96 | /***/ "./src/renderStyleSheet.js": 97 | /*!*********************************!*\ 98 | !*** ./src/renderStyleSheet.js ***! 99 | \*********************************/ 100 | /*! exports provided: default */ 101 | /***/ (function(module, __webpack_exports__, __webpack_require__) { 102 | 103 | "use strict"; 104 | __webpack_require__.r(__webpack_exports__); 105 | /* harmony import */ var sketch_dom__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! sketch/dom */ "sketch/dom"); 106 | /* harmony import */ var sketch_dom__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(sketch_dom__WEBPACK_IMPORTED_MODULE_0__); 107 | 108 | /* harmony default export */ __webpack_exports__["default"] = (function (context) { 109 | var document = sketch_dom__WEBPACK_IMPORTED_MODULE_0___default.a.fromNative(context.document); 110 | var pages = context.document.pages(); // Remove previous rendered pages (thanks to react-sketchapp) 111 | 112 | for (var index = pages.length - 1; index >= 0; index -= 1) { 113 | if (pages.length > 1) { 114 | String(pages[index].name()) === "Rendered Styles" && context.document.documentData().removePageAtIndex(index); 115 | } 116 | } 117 | 118 | var RenderPage = context.document.addBlankPage(); 119 | RenderPage.name = "Rendered Styles"; 120 | var previousFrame = null; // now make a page 121 | 122 | var stl = document.sharedTextStyles.length; 123 | 124 | for (var property = 0; property < stl; property++) { 125 | var textLayer = new sketch_dom__WEBPACK_IMPORTED_MODULE_0__["Text"]({ 126 | text: document.sharedTextStyles[property].name, 127 | frame: { 128 | width: 100, 129 | height: 32, 130 | x: 0, 131 | y: previousFrame != null ? Math.ceil(previousFrame.frame.height + previousFrame.frame.y + 24) : 0 132 | }, 133 | sharedStyleId: document.sharedTextStyles[property].id, 134 | style: document.sharedTextStyles[property].style, 135 | parent: RenderPage 136 | }); 137 | textLayer.name = document.sharedTextStyles[property].name; 138 | previousFrame = textLayer; 139 | } 140 | }); 141 | 142 | /***/ }), 143 | 144 | /***/ "sketch/dom": 145 | /*!*****************************!*\ 146 | !*** external "sketch/dom" ***! 147 | \*****************************/ 148 | /*! no static exports found */ 149 | /***/ (function(module, exports) { 150 | 151 | module.exports = require("sketch/dom"); 152 | 153 | /***/ }) 154 | 155 | /******/ }); 156 | if (key === 'default' && typeof exports === 'function') { 157 | exports(context); 158 | } else if (typeof exports[key] !== 'function') { 159 | throw new Error('Missing export named "' + key + '". Your command should contain something like `export function " + key +"() {}`.'); 160 | } else { 161 | exports[key](context); 162 | } 163 | } catch (err) { 164 | if (typeof process !== 'undefined' && process.listenerCount && process.listenerCount('uncaughtException')) { 165 | process.emit("uncaughtException", err, "uncaughtException"); 166 | } else { 167 | throw err 168 | } 169 | } 170 | } 171 | globalThis['onRun'] = __skpm_run.bind(this, 'default') 172 | 173 | //# sourceMappingURL=__renderStyleSheet.js.map -------------------------------------------------------------------------------- /text-to-styles.sketchplugin/Contents/Sketch/__renderStyleSheet.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["webpack://exports/webpack/bootstrap","webpack://exports/./src/renderStyleSheet.js","webpack://exports/external \"sketch/dom\""],"names":["context","document","sketch","fromNative","pages","index","length","String","name","documentData","removePageAtIndex","RenderPage","addBlankPage","previousFrame","stl","sharedTextStyles","property","textLayer","Text","text","frame","width","height","x","y","Math","ceil","sharedStyleId","id","style","parent"],"mappings":";;;;;;;;QAAA;QACA;;QAEA;QACA;;QAEA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;;QAEA;QACA;;QAEA;QACA;;QAEA;QACA;QACA;;;QAGA;QACA;;QAEA;QACA;;QAEA;QACA;QACA;QACA,0CAA0C,gCAAgC;QAC1E;QACA;;QAEA;QACA;QACA;QACA,wDAAwD,kBAAkB;QAC1E;QACA,iDAAiD,cAAc;QAC/D;;QAEA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA,yCAAyC,iCAAiC;QAC1E,gHAAgH,mBAAmB,EAAE;QACrI;QACA;;QAEA;QACA;QACA;QACA,2BAA2B,0BAA0B,EAAE;QACvD,iCAAiC,eAAe;QAChD;QACA;QACA;;QAEA;QACA,sDAAsD,+DAA+D;;QAErH;QACA;;;QAGA;QACA;;;;;;;;;;;;;AClFA;AAAA;AAAA;AAAA;AAEe,yEAASA,OAAT,EAAkB;AAC/B,MAAIC,QAAQ,GAAGC,iDAAM,CAACC,UAAP,CAAkBH,OAAO,CAACC,QAA1B,CAAf;AACA,MAAMG,KAAK,GAAGJ,OAAO,CAACC,QAAR,CAAiBG,KAAjB,EAAd,CAF+B,CAI/B;;AACA,OAAK,IAAIC,KAAK,GAAGD,KAAK,CAACE,MAAN,GAAe,CAAhC,EAAmCD,KAAK,IAAI,CAA5C,EAA+CA,KAAK,IAAI,CAAxD,EAA2D;AACzD,QAAID,KAAK,CAACE,MAAN,GAAe,CAAnB,EAAsB;AACpBC,YAAM,CAACH,KAAK,CAACC,KAAD,CAAL,CAAaG,IAAb,EAAD,CAAN,KAAgC,iBAAhC,IACER,OAAO,CAACC,QAAR,CAAiBQ,YAAjB,GAAgCC,iBAAhC,CAAkDL,KAAlD,CADF;AAED;AACF;;AAED,MAAIM,UAAU,GAAGX,OAAO,CAACC,QAAR,CAAiBW,YAAjB,EAAjB;AACAD,YAAU,CAACH,IAAX,GAAkB,iBAAlB;AAEA,MAAIK,aAAa,GAAG,IAApB,CAf+B,CAgB/B;;AAEA,MAAMC,GAAG,GAAGb,QAAQ,CAACc,gBAAT,CAA0BT,MAAtC;;AACA,OAAK,IAAIU,QAAQ,GAAG,CAApB,EAAuBA,QAAQ,GAAGF,GAAlC,EAAuCE,QAAQ,EAA/C,EAAmD;AACjD,QAAIC,SAAS,GAAG,IAAIC,+CAAJ,CAAS;AACvBC,UAAI,EAAElB,QAAQ,CAACc,gBAAT,CAA0BC,QAA1B,EAAoCR,IADnB;AAEvBY,WAAK,EAAE;AACLC,aAAK,EAAC,GADD;AAELC,cAAM,EAAE,EAFH;AAGLC,SAAC,EAAE,CAHE;AAILC,SAAC,EACCX,aAAa,IAAI,IAAjB,GACIY,IAAI,CAACC,IAAL,CAAUb,aAAa,CAACO,KAAd,CAAoBE,MAApB,GAA6BT,aAAa,CAACO,KAAd,CAAoBI,CAAjD,GAAqD,EAA/D,CADJ,GAEI;AAPD,OAFgB;AAWvBG,mBAAa,EAAE1B,QAAQ,CAACc,gBAAT,CAA0BC,QAA1B,EAAoCY,EAX5B;AAYvBC,WAAK,EAAE5B,QAAQ,CAACc,gBAAT,CAA0BC,QAA1B,EAAoCa,KAZpB;AAavBC,YAAM,EAAGnB;AAbc,KAAT,CAAhB;AAgBAM,aAAS,CAACT,IAAV,GAAiBP,QAAQ,CAACc,gBAAT,CAA0BC,QAA1B,EAAoCR,IAArD;AACAK,iBAAa,GAAGI,SAAhB;AACD;AACF,C;;;;;;;;;;;ACzCD,uC","file":"__renderStyleSheet.js","sourcesContent":[" \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n \t\t}\n \t};\n\n \t// define __esModule on exports\n \t__webpack_require__.r = function(exports) {\n \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n \t\t}\n \t\tObject.defineProperty(exports, '__esModule', { value: true });\n \t};\n\n \t// create a fake namespace object\n \t// mode & 1: value is a module id, require it\n \t// mode & 2: merge all properties of value into the ns\n \t// mode & 4: return value when already ns object\n \t// mode & 8|1: behave like require\n \t__webpack_require__.t = function(value, mode) {\n \t\tif(mode & 1) value = __webpack_require__(value);\n \t\tif(mode & 8) return value;\n \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n \t\tvar ns = Object.create(null);\n \t\t__webpack_require__.r(ns);\n \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n \t\treturn ns;\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = \"./src/renderStyleSheet.js\");\n","import sketch, { Text } from \"sketch/dom\";\n\nexport default function(context) {\n let document = sketch.fromNative(context.document);\n const pages = context.document.pages();\n\n // Remove previous rendered pages (thanks to react-sketchapp)\n for (let index = pages.length - 1; index >= 0; index -= 1) {\n if (pages.length > 1) {\n String(pages[index].name()) === \"Rendered Styles\" &&\n context.document.documentData().removePageAtIndex(index);\n }\n }\n\n let RenderPage = context.document.addBlankPage();\n RenderPage.name = \"Rendered Styles\";\n\n let previousFrame = null;\n // now make a page\n\n const stl = document.sharedTextStyles.length;\n for (let property = 0; property < stl; property++) {\n let textLayer = new Text({\n text: document.sharedTextStyles[property].name,\n frame: {\n width:100,\n height: 32,\n x: 0,\n y:\n previousFrame != null\n ? Math.ceil(previousFrame.frame.height + previousFrame.frame.y + 24)\n : 0\n },\n sharedStyleId: document.sharedTextStyles[property].id,\n style: document.sharedTextStyles[property].style,\n parent : RenderPage\n });\n\n textLayer.name = document.sharedTextStyles[property].name;\n previousFrame = textLayer;\n }\n}\n","module.exports = require(\"sketch/dom\");"],"sourceRoot":""} -------------------------------------------------------------------------------- /text-to-styles.sketchplugin/Contents/Sketch/__setupDocument.js: -------------------------------------------------------------------------------- 1 | var globalThis = this; 2 | var global = this; 3 | function __skpm_run (key, context) { 4 | globalThis.context = context; 5 | try { 6 | 7 | var exports = 8 | /******/ (function(modules) { // webpackBootstrap 9 | /******/ // The module cache 10 | /******/ var installedModules = {}; 11 | /******/ 12 | /******/ // The require function 13 | /******/ function __webpack_require__(moduleId) { 14 | /******/ 15 | /******/ // Check if module is in cache 16 | /******/ if(installedModules[moduleId]) { 17 | /******/ return installedModules[moduleId].exports; 18 | /******/ } 19 | /******/ // Create a new module (and put it into the cache) 20 | /******/ var module = installedModules[moduleId] = { 21 | /******/ i: moduleId, 22 | /******/ l: false, 23 | /******/ exports: {} 24 | /******/ }; 25 | /******/ 26 | /******/ // Execute the module function 27 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); 28 | /******/ 29 | /******/ // Flag the module as loaded 30 | /******/ module.l = true; 31 | /******/ 32 | /******/ // Return the exports of the module 33 | /******/ return module.exports; 34 | /******/ } 35 | /******/ 36 | /******/ 37 | /******/ // expose the modules object (__webpack_modules__) 38 | /******/ __webpack_require__.m = modules; 39 | /******/ 40 | /******/ // expose the module cache 41 | /******/ __webpack_require__.c = installedModules; 42 | /******/ 43 | /******/ // define getter function for harmony exports 44 | /******/ __webpack_require__.d = function(exports, name, getter) { 45 | /******/ if(!__webpack_require__.o(exports, name)) { 46 | /******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); 47 | /******/ } 48 | /******/ }; 49 | /******/ 50 | /******/ // define __esModule on exports 51 | /******/ __webpack_require__.r = function(exports) { 52 | /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { 53 | /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); 54 | /******/ } 55 | /******/ Object.defineProperty(exports, '__esModule', { value: true }); 56 | /******/ }; 57 | /******/ 58 | /******/ // create a fake namespace object 59 | /******/ // mode & 1: value is a module id, require it 60 | /******/ // mode & 2: merge all properties of value into the ns 61 | /******/ // mode & 4: return value when already ns object 62 | /******/ // mode & 8|1: behave like require 63 | /******/ __webpack_require__.t = function(value, mode) { 64 | /******/ if(mode & 1) value = __webpack_require__(value); 65 | /******/ if(mode & 8) return value; 66 | /******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; 67 | /******/ var ns = Object.create(null); 68 | /******/ __webpack_require__.r(ns); 69 | /******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value }); 70 | /******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); 71 | /******/ return ns; 72 | /******/ }; 73 | /******/ 74 | /******/ // getDefaultExport function for compatibility with non-harmony modules 75 | /******/ __webpack_require__.n = function(module) { 76 | /******/ var getter = module && module.__esModule ? 77 | /******/ function getDefault() { return module['default']; } : 78 | /******/ function getModuleExports() { return module; }; 79 | /******/ __webpack_require__.d(getter, 'a', getter); 80 | /******/ return getter; 81 | /******/ }; 82 | /******/ 83 | /******/ // Object.prototype.hasOwnProperty.call 84 | /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; 85 | /******/ 86 | /******/ // __webpack_public_path__ 87 | /******/ __webpack_require__.p = ""; 88 | /******/ 89 | /******/ 90 | /******/ // Load entry module and return exports 91 | /******/ return __webpack_require__(__webpack_require__.s = "./src/setupDocument.js"); 92 | /******/ }) 93 | /************************************************************************/ 94 | /******/ ({ 95 | 96 | /***/ "./src/setupDocument.js": 97 | /*!******************************!*\ 98 | !*** ./src/setupDocument.js ***! 99 | \******************************/ 100 | /*! exports provided: default */ 101 | /***/ (function(module, __webpack_exports__, __webpack_require__) { 102 | 103 | "use strict"; 104 | __webpack_require__.r(__webpack_exports__); 105 | /* harmony import */ var sketch_dom__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! sketch/dom */ "sketch/dom"); 106 | /* harmony import */ var sketch_dom__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(sketch_dom__WEBPACK_IMPORTED_MODULE_0__); 107 | 108 | /* harmony default export */ __webpack_exports__["default"] = (function (context) { 109 | // Remove exisiting Pages 110 | for (var index = context.document.pages().length - 1; index >= 0; index -= 1) { 111 | context.document.documentData().removePageAtIndex(index); 112 | } // Add pages 113 | 114 | 115 | var StylesPage = context.document.addBlankPage(); 116 | StylesPage.name = "Styles"; 117 | var AlignmentsPage = context.document.addBlankPage(); 118 | AlignmentsPage.name = "Alignments"; 119 | var ColorsPage = context.document.addBlankPage(); 120 | ColorsPage.name = "Colors"; // Add Text to Styles Page 121 | 122 | var textLayer1 = new sketch_dom__WEBPACK_IMPORTED_MODULE_0__["Text"]({ 123 | text: "Style 1", 124 | frame: { 125 | width: 100, 126 | height: 32, 127 | x: 0, 128 | y: 0 129 | }, 130 | style: { 131 | fontFamily: 'Helvetica', 132 | fontWeight: 8, 133 | fontSize: 24, 134 | lineHeight: 24 * 1.5, 135 | fontStyle: "normal", 136 | paragraphSpacing: 24 * 1.5, 137 | kerning: 0 138 | }, 139 | parent: StylesPage 140 | }); 141 | textLayer1.name = "Style 1"; 142 | var textLayer2 = new sketch_dom__WEBPACK_IMPORTED_MODULE_0__["Text"]({ 143 | text: "Style 2", 144 | frame: { 145 | width: 100, 146 | height: 32, 147 | x: 0, 148 | y: textLayer1.frame.height + 24 149 | }, 150 | style: { 151 | fontFamily: 'Helvetica', 152 | fontWeight: 4, 153 | fontSize: 16, 154 | lineHeight: 16 * 1.5, 155 | fontStyle: "normal", 156 | paragraphSpacing: 16 * 1.5, 157 | kerning: 0 158 | }, 159 | parent: StylesPage 160 | }); 161 | textLayer2.name = "Style 2"; // Add Alignments 162 | 163 | var alignmentLayerLeft = new sketch_dom__WEBPACK_IMPORTED_MODULE_0__["Text"]({ 164 | text: "Left", 165 | frame: { 166 | width: 100, 167 | height: 32, 168 | x: 0, 169 | y: 0 170 | }, 171 | style: { 172 | fontFamily: 'Helvetica', 173 | fontWeight: 4, 174 | fontSize: 16, 175 | lineHeight: 16 * 1.5, 176 | fontStyle: "normal", 177 | paragraphSpacing: 16 * 1.5, 178 | kerning: 0, 179 | alignment: 'left' 180 | }, 181 | parent: AlignmentsPage 182 | }); 183 | alignmentLayerLeft.name = "Left"; 184 | var alignmentLayerRight = new sketch_dom__WEBPACK_IMPORTED_MODULE_0__["Text"]({ 185 | text: "Right", 186 | frame: { 187 | width: 100, 188 | height: 32, 189 | x: 0, 190 | y: 0 191 | }, 192 | style: { 193 | fontFamily: 'Helvetica', 194 | fontWeight: 4, 195 | fontSize: 16, 196 | lineHeight: 16 * 1.5, 197 | fontStyle: "normal", 198 | paragraphSpacing: 16 * 1.5, 199 | kerning: 0, 200 | alignment: 'right' 201 | }, 202 | parent: AlignmentsPage 203 | }); 204 | alignmentLayerLeft.name = "Right"; // Add Colors 205 | 206 | new sketch_dom__WEBPACK_IMPORTED_MODULE_0__["ShapePath"]({ 207 | name: 'Black', 208 | shapeType: sketch_dom__WEBPACK_IMPORTED_MODULE_0__["ShapePath"].ShapeType.Oval, 209 | parent: ColorsPage, 210 | frame: { 211 | width: 100, 212 | height: 100, 213 | x: 0, 214 | y: 0 215 | }, 216 | style: { 217 | fills: [{ 218 | color: '#111111', 219 | fillType: sketch_dom__WEBPACK_IMPORTED_MODULE_0__["Style"].FillType.Color 220 | }], 221 | borders: [] 222 | } 223 | }); 224 | new sketch_dom__WEBPACK_IMPORTED_MODULE_0__["ShapePath"]({ 225 | name: 'Red', 226 | shapeType: sketch_dom__WEBPACK_IMPORTED_MODULE_0__["ShapePath"].ShapeType.Oval, 227 | parent: ColorsPage, 228 | frame: { 229 | width: 100, 230 | height: 100, 231 | x: 124, 232 | y: 0 233 | }, 234 | style: { 235 | fills: [{ 236 | color: '#cc0000', 237 | fillType: sketch_dom__WEBPACK_IMPORTED_MODULE_0__["Style"].FillType.Color 238 | }], 239 | borders: [] 240 | } 241 | }); 242 | }); 243 | 244 | /***/ }), 245 | 246 | /***/ "sketch/dom": 247 | /*!*****************************!*\ 248 | !*** external "sketch/dom" ***! 249 | \*****************************/ 250 | /*! no static exports found */ 251 | /***/ (function(module, exports) { 252 | 253 | module.exports = require("sketch/dom"); 254 | 255 | /***/ }) 256 | 257 | /******/ }); 258 | if (key === 'default' && typeof exports === 'function') { 259 | exports(context); 260 | } else if (typeof exports[key] !== 'function') { 261 | throw new Error('Missing export named "' + key + '". Your command should contain something like `export function " + key +"() {}`.'); 262 | } else { 263 | exports[key](context); 264 | } 265 | } catch (err) { 266 | if (typeof process !== 'undefined' && process.listenerCount && process.listenerCount('uncaughtException')) { 267 | process.emit("uncaughtException", err, "uncaughtException"); 268 | } else { 269 | throw err 270 | } 271 | } 272 | } 273 | globalThis['onRun'] = __skpm_run.bind(this, 'default') 274 | 275 | //# sourceMappingURL=__setupDocument.js.map -------------------------------------------------------------------------------- /text-to-styles.sketchplugin/Contents/Sketch/__setupDocument.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["webpack://exports/webpack/bootstrap","webpack://exports/./src/setupDocument.js","webpack://exports/external \"sketch/dom\""],"names":["context","index","document","pages","length","documentData","removePageAtIndex","StylesPage","addBlankPage","name","AlignmentsPage","ColorsPage","textLayer1","Text","text","frame","width","height","x","y","style","fontFamily","fontWeight","fontSize","lineHeight","fontStyle","paragraphSpacing","kerning","parent","textLayer2","alignmentLayerLeft","alignment","alignmentLayerRight","ShapePath","shapeType","ShapeType","Oval","fills","color","fillType","Style","FillType","Color","borders"],"mappings":"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","file":"__setupDocument.js","sourcesContent":[" \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n \t\t}\n \t};\n\n \t// define __esModule on exports\n \t__webpack_require__.r = function(exports) {\n \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n \t\t}\n \t\tObject.defineProperty(exports, '__esModule', { value: true });\n \t};\n\n \t// create a fake namespace object\n \t// mode & 1: value is a module id, require it\n \t// mode & 2: merge all properties of value into the ns\n \t// mode & 4: return value when already ns object\n \t// mode & 8|1: behave like require\n \t__webpack_require__.t = function(value, mode) {\n \t\tif(mode & 1) value = __webpack_require__(value);\n \t\tif(mode & 8) return value;\n \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n \t\tvar ns = Object.create(null);\n \t\t__webpack_require__.r(ns);\n \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n \t\treturn ns;\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = \"./src/setupDocument.js\");\n","import { Text, ShapePath, Style } from \"sketch/dom\";\n\nexport default function(context) {\n \n // Remove exisiting Pages\n for (let index = context.document.pages().length - 1; index >= 0; index -= 1) {\n \n context.document.documentData().removePageAtIndex(index);\n \n }\n\n // Add pages\n const StylesPage = context.document.addBlankPage();\n StylesPage.name = \"Styles\";\n \n const AlignmentsPage = context.document.addBlankPage();\n AlignmentsPage.name = \"Alignments\";\n \n const ColorsPage = context.document.addBlankPage();\n ColorsPage.name = \"Colors\";\n\n\n // Add Text to Styles Page\n const textLayer1 = new Text({\n text: \"Style 1\",\n frame: {\n width: 100,\n height: 32,\n x: 0,\n y: 0\n },\n style: {\n fontFamily: 'Helvetica',\n fontWeight: 8,\n fontSize: 24,\n lineHeight: 24 * 1.5,\n fontStyle: \"normal\",\n paragraphSpacing: 24*1.5,\n kerning : 0\n },\n parent : StylesPage\n });\n textLayer1.name = \"Style 1\";\n\n const textLayer2 = new Text({\n text: \"Style 2\",\n frame: {\n width: 100,\n height: 32,\n x: 0,\n y: textLayer1.frame.height + 24\n },\n style: {\n fontFamily: 'Helvetica',\n fontWeight: 4,\n fontSize: 16,\n lineHeight: 16 * 1.5,\n fontStyle: \"normal\",\n paragraphSpacing: 16*1.5,\n kerning : 0\n },\n parent : StylesPage\n });\n textLayer2.name = \"Style 2\";\n\n // Add Alignments\n const alignmentLayerLeft = new Text({\n text: \"Left\",\n frame: {\n width: 100,\n height: 32,\n x: 0,\n y: 0\n },\n style: {\n fontFamily: 'Helvetica',\n fontWeight: 4,\n fontSize: 16,\n lineHeight: 16 * 1.5,\n fontStyle: \"normal\",\n paragraphSpacing: 16*1.5,\n kerning : 0,\n alignment : 'left'\n },\n parent : AlignmentsPage\n });\n alignmentLayerLeft.name = \"Left\";\n\n const alignmentLayerRight = new Text({\n text: \"Right\",\n frame: {\n width: 100,\n height: 32,\n x: 0,\n y: 0\n },\n style: {\n fontFamily: 'Helvetica',\n fontWeight: 4,\n fontSize: 16,\n lineHeight: 16 * 1.5,\n fontStyle: \"normal\",\n paragraphSpacing: 16*1.5,\n kerning : 0,\n alignment : 'right'\n },\n parent : AlignmentsPage\n });\n alignmentLayerLeft.name = \"Right\";\n\n // Add Colors\n new ShapePath({\n name: 'Black',\n shapeType: ShapePath.ShapeType.Oval,\n parent : ColorsPage,\n frame : {\n width: 100,\n height: 100,\n x: 0, \n y : 0\n },\n style : {\n fills : [\n {\n color: '#111111',\n fillType: Style.FillType.Color,\n }\n ],\n borders : []\n }\n });\n\n new ShapePath({\n name: 'Red',\n shapeType: ShapePath.ShapeType.Oval,\n parent : ColorsPage,\n frame : {\n width: 100,\n height: 100,\n x: 124, \n y : 0\n },\n style : {\n fills : [\n {\n color: '#cc0000',\n fillType: Style.FillType.Color,\n }\n ],\n borders : []\n }\n });\n \n}\n","module.exports = require(\"sketch/dom\");"],"sourceRoot":""} -------------------------------------------------------------------------------- /text-to-styles.sketchplugin/Contents/Sketch/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "compatibleVersion": 3, 3 | "bundleVersion": 1, 4 | "icon": "icon.png", 5 | "name": "Text to Styles", 6 | "description": "Generates Sketch Text Styles from Layers to easily manage, size, colour and alignment variations", 7 | "commands": [ 8 | { 9 | "name": "Create Text Styles", 10 | "identifier": "createTextStyles", 11 | "script": "__createTextStyles.js" 12 | } 13 | ], 14 | "menu": { 15 | "title": "Text to Styles", 16 | "items": [ 17 | "runDocumentSetup", 18 | "createTextStyles", 19 | "renderStyleSheet", 20 | "renderJSON" 21 | ] 22 | }, 23 | "version": "4.0.0", 24 | "identifier": "Text to Styles", 25 | "disableCocoaScriptPreprocessor": true, 26 | "appcast": "https://raw.githubusercontent.com/tbrasington/text-to-styles/master/.appcast.xml", 27 | "author": "Thomas Brasington", 28 | "authorEmail": "tbrasington@gmail.com" 29 | } --------------------------------------------------------------------------------