├── .gitignore ├── CHANGELOG.md ├── Examples ├── Example1 │ └── ProjectDir │ │ └── tokens │ │ ├── myTokens.json │ │ └── myTokens.scss ├── Example2 │ └── ProjectDir │ │ └── tokens │ │ ├── colors.js │ │ ├── colors.scss │ │ ├── fontSizes.json │ │ └── fontSizes.scss ├── Example3 │ └── ProjectDir │ │ ├── sass │ │ ├── colors.sass │ │ └── fontSizes.sass │ │ └── tokens │ │ ├── colors.js │ │ └── fontSizes.json ├── Example4 │ └── ProjectDir │ │ └── tokens │ │ ├── myTokens.json │ │ └── myTokensRenamed.scss └── Example5 │ └── ProjectDir │ ├── scss │ ├── mergedTokenFiles.scss │ └── mergedTokenFilesAndObjects.scss │ └── tokens │ ├── colors.js │ └── fontSizes.json ├── LICENSE.md ├── README.md ├── bin └── cli.js ├── lib ├── jsJsonFilesToSassScssFiles.js ├── jsValueToSassString.js └── utils │ ├── inspection │ ├── isBoolean.js │ ├── isNull.js │ ├── isPositiveInteger.js │ ├── isString.js │ └── isUndefined.js │ ├── path │ ├── basename.js │ ├── createPathDirectories.js │ ├── hasExtension.js │ └── removeExtension.js │ └── transformation │ └── flattenObject.js ├── package.json └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .idea 3 | dist 4 | node_modules 5 | test.* 6 | test 7 | test/*.* -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # CHANGE LOG 2 | 3 | ### Release 1.6.2 4 | - Fixed bug occurring when more than 2 files are being merged and the merge object option (--mo) is activated. 5 | - see https://github.com/rlapoele/json-to-scss/issues/10 6 | 7 | ### Release 1.6.1 8 | - Fixed typos in CLI (json-to-scss) help text & README.md 9 | 10 | ### Release 1.6.0 11 | - New Features: 12 | - added option allowing users to obtain a list of sass/scss variables obtained from a flattened JSON/js object. 13 | - flattened keys can either be in kebab-case (default) or camel-case. 14 | - when flattening with kebab-case, json-to-scss does currently not change the nested key case. 15 | - when flattening with came-case, json-to-scss does currently only uppercase the first letter of the nested keys. 16 | 17 | ### Release 1.5.0 18 | - New Feature: 19 | - added option (i.e. --sk='font-family') allowing users to force matching JSON/js object key values to be quoted. 20 | 21 | ### Release 1.4.0 22 | - Upgraded dependencies. 23 | - New Features: 24 | - added option allowing users to ask for converted Sass map keys to be wrapped in single or double quote characters. 25 | - added option allowing users to ask for converted Sass map values (other than nested maps) to be wrapped in single or double quote characters. 26 | 27 | ### Release 1.3.1 28 | - README.md typo fixes for example 5.2. 29 | - command line output format updates for merge object scenario. 30 | 31 | ### Release 1.3.0 32 | - New Feature: 33 | - added possibility to merge multiple converted sources into one single destination file. 34 | - in this context, the prefix & suffix are repeated for each converted source content; 35 | - the destination file therefore contains as many prefixed & suffixed block as there are sources. 36 | - if no prefix is specified then each source file name becomes a prefix ($source-file-name: ...). 37 | - by default, each converted source becomes a distinct/separated sass variable in the destination file. 38 | - when merging multiple source files, added possibility to merge content as well using global option "--mo" (merge object); 39 | - in this context, the prefix & suffix are only used once in the destination file and each individual converted block are comma+linefeed separated. 40 | - Code modifications: 41 | - further simplified code when possible (remove levels of nested internal function declarations). 42 | - updated object property existence checks such as "if (property.object)..." and used "'property' in object ..." expressions instead. 43 | - converted all switch statements into function + literal object declarations. 44 | 45 | ### Release 1.2.4 46 | - Code modifications 47 | - simplified code by removing all options & config objects as well unnecessary validations. 48 | - Remove TextIndentation.js file/function (use String.prototype.repeat(...) directly instead). 49 | 50 | ### Release 1.2.3 51 | - Code cleanups & comments addition. 52 | 53 | ### Release 1.2.2 54 | - Code Fixes: 55 | - changed jsJsonFilesToSassScssFiles default config to ensure fn input validation flag is set to true by default. 56 | - updated cli.js to handle above change. 57 | - fixed typo in jsJsonFilesToSassScssFiles.js input validation (sourceFilepaths arg was checked 2 times instead of sourceFilepaths check + destinationFilepaths check.) 58 | - Enhancements: 59 | - re-organized jsJsonFilesToSassScssFiles code to be more functional. 60 | 61 | ### Release 1.2.1 62 | - Documentation updates. 63 | - Enhancements: 64 | - improved function input param validation 65 | - leveraged const default config. 66 | 67 | ### Release 1.2.0 68 | - New Feature: 69 | - expanded definition of .js/.json self config ('_jsonToScssConfig') to add a new 'filename' property. 70 | - this allows users to define custom destination file name directly within the .js/.json file(s) they want to convert to sass or scss. 71 | - Enhancements: 72 | - Added error message when specified source is invalid. This prevents situation when source is invalid and app appears to react as if nothing happened. 73 | - Updates: 74 | - remove jsdoc docs - users can still generate it from their side if interested. 75 | - added an Examples folder with 4 examples. 76 | - added explanations and examples to README.md. 77 | 78 | ### Release 1.1.1 79 | - Added code documentation & generated it with _jsdoc_ in the `docs` folder. 80 | 81 | ### Release 1.1.0 82 | - New Features: 83 | - auto-detection of a '_jsonToScssConfig' property within .js/.json files: 84 | - this allows users to define a local configuration for individual .js/.json files; when detected this _local_ config (options) takes precedence over the command line options. 85 | - new CLI option '**--no-underscore**' allowing users to tell _json-to-scss_ to remove any leading `_` (underscore) character from the prefix when the later corresponds to a sass variable. 86 | - Fixes/Enhancements 87 | - added missing support for empty string setup options. 88 | - wrapped "require(content.js/.json) ..." code portion in try / catch to improve code robustness. 89 | - removed what seems to be an unnecessary json to string & string to json conversion. 90 | - Updates 91 | - README.md updates to reflect the updated usage and so on. 92 | 93 | ### Release 1.0.2 94 | - Fixes 95 | - swapped package.json "bin" & "main" values. 96 | - added a missing CHANGELOG.md file. 97 | 98 | ### Release 1.0.1 99 | - Cleanups 100 | - removed experimental index.js. 101 | -------------------------------------------------------------------------------- /Examples/Example1/ProjectDir/tokens/myTokens.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors": { 3 | "primary-color": "#FFFFFF", 4 | "accent-color": "#0099FF" 5 | }, 6 | "font-sizes": { 7 | "small": ".875rem", 8 | "medium": "1rem", 9 | "large": "2rem" 10 | }, 11 | "web-browser-default-font-size": "16px" 12 | } 13 | -------------------------------------------------------------------------------- /Examples/Example1/ProjectDir/tokens/myTokens.scss: -------------------------------------------------------------------------------- 1 | $myTokens: ( 2 | colors: ( 3 | primary-color: #FFFFFF, 4 | accent-color: #0099FF 5 | ), 6 | font-sizes: ( 7 | small: .875rem, 8 | medium: 1rem, 9 | large: 2rem 10 | ), 11 | web-browser-default-font-size: 16px 12 | ); 13 | -------------------------------------------------------------------------------- /Examples/Example2/ProjectDir/tokens/colors.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | colors: { 3 | "primary-color": "#FFFFFF", 4 | "accent-color": "#0099FF" 5 | } 6 | }; -------------------------------------------------------------------------------- /Examples/Example2/ProjectDir/tokens/colors.scss: -------------------------------------------------------------------------------- 1 | $colors: ( 2 | colors: ( 3 | primary-color: #FFFFFF, 4 | accent-color: #0099FF 5 | ) 6 | ); 7 | -------------------------------------------------------------------------------- /Examples/Example2/ProjectDir/tokens/fontSizes.json: -------------------------------------------------------------------------------- 1 | { 2 | "font-sizes": { 3 | "small": ".875rem", 4 | "medium": "1rem", 5 | "large": "2rem" 6 | }, 7 | "web-browser-default-font-size": "16px" 8 | } -------------------------------------------------------------------------------- /Examples/Example2/ProjectDir/tokens/fontSizes.scss: -------------------------------------------------------------------------------- 1 | $fontSizes: ( 2 | font-sizes: ( 3 | small: .875rem, 4 | medium: 1rem, 5 | large: 2rem 6 | ), 7 | web-browser-default-font-size: 16px 8 | ); 9 | -------------------------------------------------------------------------------- /Examples/Example3/ProjectDir/sass/colors.sass: -------------------------------------------------------------------------------- 1 | $colors: (colors: (primary-color: #FFFFFF, accent-color: #0099FF)) 2 | -------------------------------------------------------------------------------- /Examples/Example3/ProjectDir/sass/fontSizes.sass: -------------------------------------------------------------------------------- 1 | $fontSizes: (font-sizes: (small: .875rem, medium: 1rem, large: 2rem), web-browser-default-font-size: 16px) 2 | -------------------------------------------------------------------------------- /Examples/Example3/ProjectDir/tokens/colors.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | colors: { 3 | "primary-color": "#FFFFFF", 4 | "accent-color": "#0099FF" 5 | } 6 | }; -------------------------------------------------------------------------------- /Examples/Example3/ProjectDir/tokens/fontSizes.json: -------------------------------------------------------------------------------- 1 | { 2 | "font-sizes": { 3 | "small": ".875rem", 4 | "medium": "1rem", 5 | "large": "2rem" 6 | }, 7 | "web-browser-default-font-size": "16px" 8 | } -------------------------------------------------------------------------------- /Examples/Example4/ProjectDir/tokens/myTokens.json: -------------------------------------------------------------------------------- 1 | { 2 | "_jsonToScss": { 3 | "sassVariableName": "__example-4", 4 | "filename": "myTokensRenamed", 5 | "prefix": "garbage", 6 | "suffix": "; // an scss comment.", 7 | "emptyString": "''", 8 | "indentationText": " ", 9 | "indentationSize": 2, 10 | "noUnderscore": true 11 | 12 | }, 13 | "colors": { 14 | "primary-color": "#FFFFFF", 15 | "accent-color": "#0099FF" 16 | }, 17 | "font-sizes": { 18 | "small": ".875rem", 19 | "medium": "1rem", 20 | "large": "2rem" 21 | 22 | }, 23 | "example-of-empty-string": "", 24 | "web-browser-default-font-size": "16px" 25 | } 26 | -------------------------------------------------------------------------------- /Examples/Example4/ProjectDir/tokens/myTokensRenamed.scss: -------------------------------------------------------------------------------- 1 | $example-4: ( 2 | colors: ( 3 | primary-color: #FFFFFF, 4 | accent-color: #0099FF 5 | ), 6 | font-sizes: ( 7 | small: .875rem, 8 | medium: 1rem, 9 | large: 2rem 10 | ), 11 | example-of-empty-string: '', 12 | web-browser-default-font-size: 16px 13 | ); // an scss comment. 14 | -------------------------------------------------------------------------------- /Examples/Example5/ProjectDir/scss/mergedTokenFiles.scss: -------------------------------------------------------------------------------- 1 | $colors: ( 2 | colors: ( 3 | primary-color: #FFFFFF, 4 | accent-color: #0099FF 5 | ) 6 | ); 7 | $fontSizes: ( 8 | font-sizes: ( 9 | small: .875rem, 10 | medium: 1rem, 11 | large: 2rem 12 | ), 13 | web-browser-default-font-size: 16px 14 | ); 15 | -------------------------------------------------------------------------------- /Examples/Example5/ProjectDir/scss/mergedTokenFilesAndObjects.scss: -------------------------------------------------------------------------------- 1 | $mergedTokenFilesAndObjects: ( 2 | colors: ( 3 | primary-color: #FFFFFF, 4 | accent-color: #0099FF 5 | ), 6 | font-sizes: ( 7 | small: .875rem, 8 | medium: 1rem, 9 | large: 2rem 10 | ), 11 | web-browser-default-font-size: 16px 12 | ); 13 | -------------------------------------------------------------------------------- /Examples/Example5/ProjectDir/tokens/colors.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | colors: { 3 | 'primary-color': '#FFFFFF', 4 | 'accent-color': '#0099FF' 5 | } 6 | }; 7 | -------------------------------------------------------------------------------- /Examples/Example5/ProjectDir/tokens/fontSizes.json: -------------------------------------------------------------------------------- 1 | { 2 | "font-sizes": { 3 | "small": ".875rem", 4 | "medium": "1rem", 5 | "large": "2rem" 6 | }, 7 | "web-browser-default-font-size": "16px" 8 | } -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | MIT License 3 | 4 | Copyright © 2018-present, Renaud Lapoële 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # JSON-TO-SCSS 2 |

3 | Downloads 4 | Version 5 | License 6 |

7 | 8 | > Convert your js & json files to sass or scss files. 9 | 10 | A small utility to convert js & json file(s) to scss/sass file(s). 11 | 12 | ## Motivation 13 | This library has initially been created to contribute to & facilitate the maintenance of living style guides. 14 | 15 | As far as living style guides go, defining and using an agreed upon a set of design properties is a good starting point and best practice (c.f. [_design tokens_](https://uxdesign.cc/design-tokens-for-dummies-8acebf010d71), [_Salesforce Lightning Design System_](https://developer.salesforce.com/docs/atlas.en-us.lightning.meta/lightning/tokens_intro.htm), etc... ). 16 | 17 | While many file formats can be used (YAML, TOML, etc...) to store such properties, it is very easy to use javascript or json files. 18 | 19 | Now, defining & storing design properties in one place is one thing but it is obviously useless if it cannot be easily consumed. 20 | 21 | Developers will find it convenient to work with .js or .json file but what about others and, for example, people interested in leveraging such props in SASS for instance? 22 | 23 | This is where _json-to-scss_ comes into play; this is obviously not the first (nor last) conversion tool (see below) but this is mine and I hope that you will find it useful. :) 24 | 25 | [Andrew Clark](https://github.com/acdlite)'s [json-sass](https://github.com/acdlite/json-sass) library has been a significant source of inspiration for this version. Feel free to check-it out. 26 | 27 | 28 | ## Installation 29 | To use _json-to-scss_ as development dependency with... 30 | 31 | #### Yarn 32 | ```shell 33 | yarn add -D json-to-scss 34 | ``` 35 | 36 | #### Npm 37 | ```shell 38 | npm install json-to-scss --save-dev 39 | ``` 40 | 41 | ## Usage 42 | ``` 43 | Usage: json-to-scss [destination] [options] 44 | 45 | source: the path to a javascript, json or group of files to be converted. 46 | (required) - only '.js' and '.json' are processed. 47 | 48 | destination: the full or partial destination of the converted files. 49 | (optional) - when the destination is a directory path only, all generated 50 | files are saved in it with a default '.scss' extension. If 51 | a '.sass' extension is required instead, the --sass option must be included. 52 | 53 | 54 | options: 55 | 56 | --h (help) Show this message. 57 | --p='prefix' (prefix) Prepend the converted sass/scss content with the prefix. 58 | Prefix is usually used & set to be used as sass variable name. 59 | Default '${source-filename} :'. 60 | --no-underscore (no leading _) Remove any leading '_' (underscore) characters from the 61 | prefix when used as sass variable name. 62 | --s='suffix' (suffix) Append the converted sass/scss content with the suffix. 63 | Default: ';' (default not used if --sass) 64 | --tt='tabText' (tab text) Text to be used to indent or tabulate sass map. 65 | Default: ' ' (two space characters) 66 | --tn=tabNumber (tab number) Number of tabulations. 67 | Default: 1 (set to 0 if --sass) 68 | --es='sq'||'dq' (empty string) Sass/scss representation for an empty string (single or double quote). 69 | Default is '""': { "prop": "" } => $xyzfilename: ( prop: "" ); 70 | --sass (sass ext.) Use sass extension. 71 | --mo (merge objects) Merge obtained sass strings into a single sass map/list. 72 | Enabled only if destination contains a full file name (name + .ext) 73 | --k='auto'|| (sass map keys) Sass/scss format for map keys. 74 | 'sq'||'dq' 'auto' (default): keys are formatted as per their converted type (number, ...) 75 | 'sq': all keys are single quoted. 76 | 'dq': all keys are doubled quoted. 77 | --v='auto'|| (sass map val.) Sass/scss format for map values other than nested maps. 78 | 'sq'||'dq' 'auto' (default): values are formatted as per their converted type 79 | 'sq': all values are single quoted. 80 | 'dq': all values are doubled quoted. 81 | Notes regarding 'sq' or 'dq' usage: 82 | 1- nested quote characters are automatically replaced by their counterpart. 83 | { "prop": "Arial, 'sans-serif'"} with 'sq' => ( prop: 'Arial, "sans-serif"' ); 84 | 2- empty strings are formatted as per the given 'sq' or 'dq' option value regardless 85 | of the --es option. 86 | --sk='family,..' (string keys) Comma separated property names (keys) for which values must be quoted. 87 | Property names are case insensitive (fontFamily will be treated like FontFamily, etc...). 88 | Only non-object value keys are compared against the "string keys": 89 | { "key 1": { "key 2": "some,possible,values" }} => only "key 2" will be considered. 90 | Default "string keys" (property names) are: 91 | family,font-family,fontfamily,stack,font-stack,fontstack,face,font-face,fontface 92 | Turn this option off by setting it to '' (e.g. --sk=''). 93 | --fk (flatten keys) Flatten JSON/js object keys to produce series of sass/scss variables instead of a map. 94 | Provided prefix and suffix, if any, are applied to each flatten key. 95 | Key name elements (nested JSON object props) are dash separated (kebab-case). 96 | In case of flatten key name conflict(s), the latest processed key value is used. 97 | This option is not available in the js/JSON embed-able config. 98 | --fkc='kebab'|| (flat. key case) Flattened key case. 99 | 'camel' 'kebab' (default): nested keys are dash separated. No letter case change. 100 | 'camel': top level keys are left as-is whereas nested keys are capitalized before 101 | being concatenated. The nested key capitalization does not change the case of the 102 | subsequent letters: 'hEllO' => 'HEllO'. 103 | 104 | 105 | ``` 106 | 107 | ## Regarding `.js` files 108 | 109 | _json-to-scss_ can convert `.js` files as long as these are a nodejs modules exporting a **javascript object**. 110 | 111 | #### Javascript (node module) file sample 112 | 113 | ```js 114 | const colorRed = "#FF0000"; 115 | const colorBlue = "#0099FF"; 116 | 117 | module.exports = { 118 | colors: { 119 | red: colorRed, 120 | green: "#00FF00", 121 | blue: colorBlue 122 | } 123 | } 124 | ``` 125 | 126 | ## Examples 127 | 128 | #### Example #1 129 | 130 | This example shows how to convert a single specific file using the default _json-to-scss_ options and storing the converted file in the same source directory. 131 | 132 | ##### Directory Structure: 133 | 134 | ``` 135 | . 136 | ├─ Examples 137 | │ ├─ Example1 138 | │ │ └─ ProjectDir 139 | │ │ └─ tokens 140 | │ │ └─ myTokens.json 141 | . . 142 | ``` 143 | 144 | ##### myTokens.json 145 | 146 | ```json 147 | { 148 | "colors": { 149 | "primary-color": "#FFFFFF", 150 | "accent-color": "#0099FF" 151 | }, 152 | "font-sizes": { 153 | "small": ".875rem", 154 | "medium": "1rem", 155 | "large": "2rem" 156 | }, 157 | "font-family": { 158 | "sans-serif": "'Roboto, Helvetica, Arial, sans-serif'" 159 | }, 160 | "web-browser-default-font-size": "16px" 161 | } 162 | ``` 163 | 164 | Note: values corresponding to sass/scss list and which you cannot or do not want to store as an array in your JSON should be quoted such as "'my, list, of, values'". 165 | 166 | ##### Command: 167 | 168 | ``` 169 | $ json-to-scss ./Examples/Example1/ProjectDir/tokens/myTokens.json 170 | $ json-to-scss vX.Y.Z 171 | $ /.../Examples/Example1/ProjectDir/tokens/myTokens.json: content converted. File created! 172 | $ /.../Examples/Example1/ProjectDir/tokens/myTokens.scss 173 | ``` 174 | 175 | ##### Results: 176 | 177 | ###### Directory Structure: 178 | 179 | ``` 180 | . 181 | ├─ Examples 182 | │ ├─ Example1 183 | │ │ └─ ProjectDir 184 | │ │ └─ tokens 185 | │ │ ├─ myTokens.json 186 | │ │ └─ myTokens.scss 187 | . . 188 | ``` 189 | 190 | ###### myTokens.scss 191 | 192 | ```scss 193 | $myTokens: ( 194 | colors: ( 195 | primary-color: #FFFFFF, 196 | accent-color: #0099FF 197 | ), 198 | font-sizes: ( 199 | small: .875rem, 200 | medium: 1rem, 201 | large: 2rem 202 | ), 203 | font-family: ( 204 | sans-serif: 'Roboto, Helvetica, Arial, sans-serif' 205 | ), 206 | web-browser-default-font-size: 16px 207 | ); 208 | ``` 209 | 210 | #### Example #2 211 | In this example, we will demonstrate _json-to-scss_ ability to accept glob patterns. 212 | 213 | Note that when using a glob pattern, the source argument must be wrapped in single quotes such as `'...**/*.*'`. 214 | 215 | ##### Directory Structure: 216 | 217 | ``` 218 | . 219 | ├─ Examples 220 | │ ├─ Example2 221 | │ │ └─ ProjectDir 222 | │ │ └─ tokens 223 | │ │ ├─ colors.js 224 | │ │ └─ fontSizes.js 225 | . . 226 | ``` 227 | 228 | ##### colors.js 229 | ```js 230 | module.exports = { 231 | colors: { 232 | "primary-color": "#FFFFFF", 233 | "accent-color": "#0099FF" 234 | } 235 | }; 236 | ``` 237 | 238 | ##### fontSizes.json 239 | ```json 240 | { 241 | "font-sizes": { 242 | "small": ".875rem", 243 | "medium": "1rem", 244 | "large": "2rem" 245 | }, 246 | "web-browser-default-font-size": "16px" 247 | } 248 | ``` 249 | 250 | ##### Command: 251 | 252 | ``` 253 | $ json-to-scss './Examples/Example2/**/*.*' 254 | $ json-to-scss vX.Y.Z 255 | $ /.../Examples/Example2/ProjectDir/tokens/colors.js: content converted. File created! 256 | $ /.../Examples/Example2/ProjectDir/tokens/colors.scss 257 | $ /.../Examples/Example2/ProjectDir/tokens/fontSizes.json: content converted. File created! 258 | $ /.../Examples/Example2/ProjectDir/tokens/fontSizes.scss 259 | ``` 260 | 261 | ##### Results: 262 | 263 | ###### Directory Structure: 264 | 265 | ``` 266 | . 267 | ├─ Examples 268 | │ ├─ Example2 269 | │ │ └─ ProjectDir 270 | │ │ └─ tokens 271 | │ │ ├─ colors.js 272 | │ │ ├─ colors.js 273 | │ │ ├─ fontSizes.json 274 | │ │ └─ fontSizes.scss 275 | . . 276 | ``` 277 | 278 | ###### colors.scss 279 | 280 | ```scss 281 | $colors: ( 282 | colors: ( 283 | primary-color: #FFFFFF, 284 | accent-color: #0099FF 285 | ) 286 | ); 287 | ``` 288 | 289 | 290 | ###### fontSizes.scss 291 | 292 | ```scss 293 | $fontSizes: ( 294 | font-sizes: ( 295 | small: .875rem, 296 | medium: 1rem, 297 | large: 2rem 298 | ), 299 | web-browser-default-font-size: 16px 300 | ); 301 | ``` 302 | 303 | #### Example #3 304 | This example will target a similar directory source structure & the same set of files however, this time, we will specify a `sass` target directory for the converted files and request _json-to-scss_ to format the output in `sass` format. 305 | 306 | Additionally, we will ask _json-to-scss_ to use a tab text (_--tt option_) such as `' '` (4 spaces) and to use a tab number/size (_--tn option_) of `5`... 307 | 308 | ##### Directory Structure: 309 | 310 | ``` 311 | . 312 | ├─ Examples 313 | │ ├─ Example3 314 | │ │ └─ ProjectDir 315 | │ │ └─ tokens 316 | │ │ ├─ colors.js 317 | │ │ └─ fontSizes.js 318 | . . 319 | ``` 320 | 321 | ##### Command: 322 | 323 | ``` 324 | $ json-to-scss './Examples/Example3/**/*.*' ./Examples/Example3/ProjectDir/sass --sass --tt=' ' --tn=5 325 | $ json-to-scss vX.Y.Z 326 | $ /.../Examples/Example3/ProjectDir/tokens/colors.js: content converted. File created! 327 | $ /.../Examples/Example3/ProjectDir/sass/colors.sass 328 | $ /.../Examples/Example3/ProjectDir/tokens/fontSizes.json: content converted. File created! 329 | $ /.../Examples/Example3/ProjectDir/sass/fontSizes.sass 330 | ``` 331 | 332 | ##### Results: 333 | 334 | ###### Directory Structure: 335 | 336 | ``` 337 | . 338 | ├─ Examples 339 | │ ├─ Example3 340 | │ │ └─ ProjectDir 341 | │ │ ├─ sass 342 | │ │ │ ├─ colors.sass 343 | │ │ │ └─ fontSizes.sass 344 | │ │ └─ tokens 345 | │ │ ├─ colors.js 346 | │ │ └─ fontSizes.js 347 | . . 348 | ``` 349 | 350 | 351 | ###### /sass/colors.sass 352 | 353 | ```sass 354 | $colors: (colors: (primary-color: #FFFFFF, accent-color: #0099FF)) 355 | ``` 356 | 357 | ###### /sass/fontSizes.sass 358 | 359 | ```sass 360 | $fontSizes: (font-sizes: (small: .875rem, medium: 1rem, large: 2rem), web-browser-default-font-size: 16px) 361 | ``` 362 | 363 | As you can notice in the produced sass content presented above, the options related to the text indentation (_--tt_ and _--tn_) have both been ignored and, since we asked to produce sass here, the default indentation has even been removed. 364 | 365 | #### Example #4 366 | 367 | For this example, we'll reuse the same directory structure as in the first example. 368 | 369 | However this time, we will include a local conversion configuration directly within the json file that we want to convert. 370 | 371 | As you will see, this local config will overwrite/supersede the default & command line (through options) configs. 372 | 373 | ##### Directory Structure: 374 | 375 | ``` 376 | . 377 | ├─ Examples 378 | │ ├─ Example4 379 | │ │ └─ ProjectDir 380 | │ │ └─ tokens 381 | │ │ └─ myTokens.json 382 | . . 383 | ``` 384 | 385 | ##### myTokens.json: 386 | 387 | ```json 388 | { 389 | "_jsonToScss": { 390 | "sassVariableName": "__example-4", 391 | "filename": "myTokensRenamed", 392 | "prefix": "garbage", 393 | "suffix": "; // an scss comment.", 394 | "emptyString": "''", 395 | "indentationText": " ", 396 | "indentationSize": 2, 397 | "noUnderscore": true, 398 | "keyFormat": "dq", 399 | "valueFormat": "auto" 400 | }, 401 | "colors": { 402 | "primary-color": "#FFFFFF", 403 | "accent-color": "#0099FF" 404 | }, 405 | "font-sizes": { 406 | "small": ".875rem", 407 | "medium": "1rem", 408 | "large": "2rem" 409 | 410 | }, 411 | "example-of-empty-string": "", 412 | "web-browser-default-font-size": "16px" 413 | } 414 | ``` 415 | 416 | Notice the **`"_jsonToScss"`** property & object in our `myTokens.json` file. 417 | 418 | This object is treated as a local conversion configuration; let us see what properties it contains: 419 | 420 | - **sassVariableName** 421 | - this tells _json-to-scss_ to prefix the converted content using `__example-4`. Notice here that you do not need to include the `$` character since _json-to-scss_ will automatically insert it for you. 422 | 423 | - **notes:** 424 | - this feature only exists in the context of local config and there is therefore no direct equivalent option at the command line level; the command line option which could potentially yield similar results is `--prefix`. 425 | - when specified, the `"sassVariableName"` property value takes precedence over the `"prefix"` property value (and therefore equivalent command line option `--prefix`). 426 | 427 | - **filename** 428 | - this informs _json-to-scss_ that the destination file will have to be renamed ("myTokensRenamed" in this example); any specified extension will be ignored. 429 | 430 | - **prefix** 431 | - allows one to define or locally override the content prefix. In this example, the "garbage" value will be ignored due to the definition of **sassVariableName** in the same local configuration. 432 | 433 | - **suffix** 434 | - allows one to define or locally override the content suffix. In this example, "; // an scss comment." will be appended to "myTokens.json" converted content. 435 | 436 | - **emptyString** 437 | - tells _json-to-scss_ how to format sass values equal to empty strings. By default and here too, empty string values are represented as `''` (two single quotes) 438 | 439 | - **indentationText** 440 | - specifies the portion of text to be used as indentation "space". Here, `" "` (two white spaces) is set as the indentation text. 441 | 442 | - **indentationSize** 443 | - indicates the number of indentation "space"(s) which must be used when indenting content; in our example, since the value is 2, it will indent nested sass maps/values with 2 "space" text chunks per indentation level. 444 | 445 | - **noUnderscore** 446 | - when set to `true` (as it is the case in our example), this tells _json-to-scss_ to remove any `_` (underscore) character possibly present in the prefix and if such a prefix starts with `$_`. The same result can be achieved for all converted files using the command line option "--no-underscore". 447 | 448 | - **keyFormat** 449 | - allows one to force sass map keys to be wrapped (or not - use "auto") in single (use "sq") or double quote (use "dq"). 450 | 451 | - **valueFormat** 452 | - allows one to force sass map values to be wrapped (or not - use "auto") in single (use "sq") or double quote (use "dq"). 453 | 454 | ##### Command: 455 | 456 | ``` 457 | $ json-to-scss ./Examples/Example4/ProjectDir/tokens/myTokens.json 458 | $ json-to-scss vX.Y.Z 459 | $ /.../Examples/Example4/ProjectDir/tokens/myTokens.json: content converted. File created! 460 | $ /.../Examples/Example4/ProjectDir/tokens/myTokensRenamed.scss 461 | ``` 462 | 463 | ##### Results: 464 | 465 | ###### Directory Structure: 466 | 467 | ``` 468 | . 469 | ├─ Examples 470 | │ ├─ Example4 471 | │ │ └─ ProjectDir 472 | │ │ └─ tokens 473 | │ │ ├─ myTokens.json 474 | │ │ └─ myTokensRenamed.scss 475 | . . 476 | ``` 477 | 478 | Notice now how "myTokens" got renamed "myTokensRenamed". 479 | 480 | 481 | ###### myTokensRenamed.scss: 482 | 483 | ```scss 484 | $example-4: ( 485 | "colors": ( 486 | "primary-color": #FFFFFF, 487 | "accent-color": #0099FF 488 | ), 489 | "font-sizes": ( 490 | "small": .875rem, 491 | "medium": 1rem, 492 | "large": 2rem 493 | ), 494 | "example-of-empty-string": '', 495 | "web-browser-default-font-size": 16px 496 | ); // an scss comment. 497 | ``` 498 | 499 | As expected, the `"_scssToJson"` local configuration property/object has been removed and the converted content has been prefixed using "__example-4". 500 | 501 | Additionally and due to the presence of `"noUnderscore": true`, all "`_`" (underscore) characters have been stripped out from the prefix/variable name after the automatically added "`$`" (dollar sign). 502 | 503 | #### Example #5 504 | 505 | This example illustrates 2 merge features (new as of v1.3.0). 506 | 507 | **Example #3**'s directory structure and source files are used. 508 | 509 | ##### #5.1 - Merging of several source files into 1 destination file: 510 | 511 | In order to merge the converted content of several source files, one must specify a destination including a file name + extension (.sass or .scss). 512 | 513 | The destination file extension is important here as this is thanks to it that _json-to-scss_ can detect the merge request... 514 | 515 | ###### Directory Structure: 516 | 517 | ``` 518 | . 519 | ├─ Examples 520 | │ ├─ Example5 521 | │ │ └─ ProjectDir 522 | │ │ └─ tokens 523 | │ │ ├─ colors.js 524 | │ │ └─ fontSizes.js 525 | . . 526 | ``` 527 | 528 | 529 | ###### Command 530 | 531 | ``` 532 | $ json-to-scss './Examples/Example5/ProjectDir/tokens/*.*' ./Examples/Example5/ProjectDir/scss/mergedTokenFiles.scss 533 | $ json-to-scss vX.Y.Z 534 | $ /.../Examples/Example5/ProjectDir/tokens/colors.js: content converted. 535 | $ /.../Examples/Example5/ProjectDir/tokens/fontSizes.json: content converted. File created! 536 | $ /.../Examples/Example5/ProjectDir/scss/mergedTokenFiles.scss 537 | ``` 538 | 539 | ###### Output 540 | 541 | ###### _Directory Structure:_ 542 | 543 | ``` 544 | . 545 | ├─ Examples 546 | │ ├─ Example5 547 | │ │ └─ ProjectDir 548 | │ │ ├─ scss 549 | │ │ │ └─ mergedTokenFiles.scss 550 | │ │ └─ tokens 551 | │ │ ├─ colors.js 552 | │ │ └─ fontSizes.js 553 | . . 554 | ``` 555 | 556 | ###### _mergedTokenFiles.scss:_ 557 | ```scss 558 | $colors: ( 559 | colors: ( 560 | primary-color: #FFFFFF, 561 | accent-color: #0099FF 562 | ) 563 | ); 564 | $fontSizes: ( 565 | font-sizes: ( 566 | small: .875rem, 567 | medium: 1rem, 568 | large: 2rem 569 | ), 570 | web-browser-default-font-size: 16px 571 | ); 572 | ``` 573 | 574 | 575 | ##### #5.2 - Merging of several source files AND of the converted content into 1 sass map/block. 576 | 577 | Note that in addition to specifying one specific destination file, we are using the `--mo` command line option here to tell _json-to-scss_ to also merge sass objects. 578 | 579 | ###### Directory Structure: 580 | 581 | ``` 582 | . 583 | ├─ Examples 584 | │ ├─ Example5 585 | │ │ └─ ProjectDir 586 | │ │ └─ tokens 587 | │ │ ├─ colors.js 588 | │ │ └─ fontSizes.js 589 | . . 590 | ``` 591 | 592 | ###### Command: 593 | 594 | ``` 595 | $ json-to-scss './Examples/Example5/ProjectDir/tokens/*.*' ./Examples/Example5/ProjectDir/scss/mergedTokenFilesAndObjects.scss --mo 596 | $ json-to-scss vX.Y.Z 597 | $ /.../Examples/Example5/ProjectDir/tokens/colors.js: content converted. 598 | $ /.../Examples/Example5/ProjectDir/tokens/fontSizes.json: content converted & merged. File created! 599 | $ /.../Examples/Example5/ProjectDir/scss/mergedTokenFilesAndObjects.scss 600 | ``` 601 | 602 | ###### Output: 603 | 604 | ###### _Directory Structure:_ 605 | 606 | ``` 607 | . 608 | ├─ Examples 609 | │ ├─ Example5 610 | │ │ └─ ProjectDir 611 | │ │ ├─ scss 612 | │ │ │ └─ mergedTokenFilesAndObjects.scss 613 | │ │ └─ tokens 614 | │ │ ├─ colors.js 615 | │ │ └─ fontSizes.js 616 | . . 617 | ``` 618 | 619 | ###### _mergedTokenFilesAndObjects.scss_ 620 | 621 | ```scss 622 | $mergedTokenFilesAndObjects: ( 623 | colors: ( 624 | primary-color: #FFFFFF, 625 | accent-color: #0099FF 626 | ), 627 | font-sizes: ( 628 | small: .875rem, 629 | medium: 1rem, 630 | large: 2rem 631 | ), 632 | web-browser-default-font-size: 16px 633 | ); 634 | ``` 635 | -------------------------------------------------------------------------------- /bin/cli.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 'use strict'; 3 | /** 4 | * JSON-TO-SCSS Command Line Interface. 5 | * @author Renaud Lapoële 6 | */ 7 | 8 | /** 9 | * Module dependencies. 10 | */ 11 | const path = require('path'); 12 | const packageJson = require(path.join(__dirname, '../package.json')); 13 | const args = require('yargs').argv; 14 | const chalk = require('chalk'); 15 | const glob = require('glob'); 16 | const pathBasename = require('../lib/utils/path/basename'); 17 | const removePathExtension = require('../lib/utils/path/removeExtension'); 18 | const isString = require('../lib/utils/inspection/isString'); 19 | const jsJsonFilesToSassScssFiles = require('../lib/jsJsonFilesToSassScssFiles'); 20 | 21 | /** 22 | * @function banner 23 | * @param {string} name - the name of the package the banner is getting built for. 24 | * @param {string} version - the version number of the package the banner is getting built for. 25 | * @returns {string} this lib/package's name & version. 26 | * @description Returns a string containing the name and the version of this lib/package. 27 | */ 28 | function banner(name, version) { 29 | return `${chalk.bold(`${name || 'NO NAME'} v${version || '0.0.0'}`)}`; 30 | } 31 | 32 | /** 33 | * @function usage 34 | * @param {string} name - the name of the package usage instructions are generated for. 35 | * @returns {string} the lib/package's usage text. 36 | * @description Returns the usage description of this lib/package. 37 | */ 38 | function usage(name) { 39 | return ` 40 | ${chalk.bold('Usage')}: ${chalk.yellow( 41 | name || 'NO NAME' 42 | )} [destination] [options] 43 | 44 | ${chalk.bold( 45 | 'source' 46 | )}: the path to a javascript, json or group of files to be converted. 47 | (required) - only '.js' and '.json' are processed. 48 | 49 | ${chalk.bold( 50 | 'destination' 51 | )}: the full or partial destination of the converted files. 52 | (optional) - when the destination is a directory path only, all generated 53 | files are saved in it with a default '.scss' extension. If 54 | a '.sass' extension is required instead, the --sass option must be included. 55 | 56 | 57 | ${chalk.bold('options')}: 58 | 59 | --h (help) Show this message. 60 | --p='prefix' (prefix) Prepend the converted sass/scss content with the prefix. 61 | Prefix is usually used & set to be used as sass variable name. 62 | Default '\${source-filename} :'. 63 | --no-underscore (no leading _) Remove any leading '_' (underscore) characters from the 64 | prefix when used as sass variable name. 65 | --s='suffix' (suffix) Append the converted sass/scss content with the suffix. 66 | Default: ';' (default not used if --sass) 67 | --tt='tabText' (tab text) Text to be used to indent or tabulate sass map. 68 | Default: ' ' (two space characters) 69 | --tn=tabNumber (tab number) Number of tabulations. 70 | Default: 1 (set to 0 if --sass) 71 | --es='sq'||'dq' (empty string) Sass/scss representation for an empty string (single or double quote). 72 | Default is '""': { "prop": "" } => $xyzfilename: ( prop: "" ); 73 | --sass (sass ext.) Use sass extension. 74 | --mo (merge objects) Merge obtained sass strings into a single sass map/list. 75 | Enabled only if destination contains a full file name (name + .ext) 76 | --k='auto'|| (sass map keys) Sass/scss format for map keys. 77 | 'sq'||'dq' 'auto' (default): keys are formatted as per their converted type (number, ...) 78 | 'sq': all keys are single quoted. 79 | 'dq': all keys are doubled quoted. 80 | --v='auto'|| (sass map val.) Sass/scss format for map values other than nested maps. 81 | 'sq'||'dq' 'auto' (default): values are formatted as per their converted type 82 | 'sq': all values are single quoted. 83 | 'dq': all values are doubled quoted. 84 | Notes regarding 'sq' or 'dq' usage: 85 | 1- nested quote characters are automatically replaced by their counterpart. 86 | { "prop": 'Arial, "sans-serif"'} with 'dq' => ( prop: "Arial, 'sans-serif'" ); 87 | 2- empty strings are formatted as per the given 'sq' or 'dq' option value regardless 88 | of the --es option. 89 | --fk (flatten keys) Flatten JSON/js object keys to produce series of sass/scss variables instead of a map. 90 | Provided prefix and suffix, if any, are applied to each flatten key. 91 | Key name elements (nested JSON object props) are dash separated (kebab-case). 92 | In case of flatten key name conflict(s), the latest processed key value is used. 93 | This option is not available in the js/JSON embed-able config. 94 | --fkc='kebab'|| (flat. key case) Flattened key case. 95 | 'camel' 'kebab' (default): nested keys are dash separated. No letter case change. 96 | 'camel': top level keys are left as-is whereas nested keys are capitalized before 97 | being concatenated. The nested key capitalization does not change the case of the 98 | subsequent letters: 'hEllO' => 'HEllO'. 99 | 100 | 101 | 102 | 103 | `; 104 | } 105 | 106 | /** 107 | * @function hasArgs 108 | * @param {object} args - command line arguments extracted via/from/with yargs. 109 | * @returns {boolean} true if args has an "_" property and if this property has a length property different than 0. 110 | * @description This is an internal small helper function to quickly assess if 'json-to-scss' is called without any params. Note that the code written here relies on the fact that we are using the 'yargs' package. 111 | */ 112 | function hasArgs(args) { 113 | return '_' in args && args._.length; 114 | } 115 | 116 | /** 117 | * @function extensionCorrector 118 | * @param {string} defaultExtension - the destination file extension to be used by default. 119 | * @param {string} requiredExtension - the destination file extension which must be used. 120 | * @returns {Function} a function to be used as input for an Array.map(fn) function call. 121 | * @description Internal helper function encapsulating the destination file extension transformations. 122 | */ 123 | function extensionCorrector(defaultExtension, requiredExtension) { 124 | return filepath => { 125 | function _correctFilepathExtension(extensionName) { 126 | let _switch = { 127 | '': () => 128 | `${filepath}${ 129 | '' !== requiredExtension ? requiredExtension : defaultExtension 130 | }`, 131 | '.scss': () => 132 | '' === requiredExtension || requiredExtension === extensionName 133 | ? filepath 134 | : `${removePathExtension(filepath)}${requiredExtension}`, 135 | '.sass': () => 136 | '' === requiredExtension || requiredExtension === extensionName 137 | ? filepath 138 | : `${removePathExtension(filepath)}${requiredExtension}`, 139 | default: () => 140 | `${removePathExtension(filepath)}${ 141 | '' !== requiredExtension ? requiredExtension : defaultExtension 142 | }` 143 | }; 144 | return (_switch[extensionName] || _switch['default'])(); 145 | } 146 | 147 | return _correctFilepathExtension(path.extname(filepath).toLowerCase()); 148 | }; 149 | } 150 | 151 | /** 152 | * @function basenameExtractor 153 | * @param {string} filepath - the file path from which we want to extract the basename. 154 | * @returns {string} the file path basename. 155 | * @description Internal helper & wrapper function extracting the file path's base name. 156 | */ 157 | function basenameExtractor(filepath) { 158 | return pathBasename(filepath); 159 | } 160 | 161 | /** 162 | * @function dirnameSetter 163 | * @param {string} dirname - the directory name we want to use for our destination file paths. 164 | * @returns {function} a function to be used as input for an Array.map(fn) function call. 165 | * @description set the directory(ies) for the given destination file path. 166 | */ 167 | function dirnameSetter(dirname) { 168 | return filepath => path.resolve(path.join(dirname, filepath)); 169 | } 170 | 171 | /** 172 | * @function normalizeArgs 173 | * @param {object} args - command line program arguments (built by the yargs package) to be normalized. 174 | * @returns {{source: {paths: *}, destination: {paths: (*|Array)}, options: {prefix: string | string, suffix: (*|string), emptyString: string, indentationText: (*|string), indentationSize: (*|number), noUnderscore: boolean, format: string}}} 175 | * @description check & normalize the command line program arguments. 176 | */ 177 | function normalizeArgs(args) { 178 | const _source = path.resolve(process.cwd(), `${args._[0]}`); 179 | const _sourcePaths = glob.sync(_source); 180 | const _defaultExtension = '.scss'; 181 | let _requiredExtension = 'sass' in args ? '.sass' : ''; 182 | 183 | const _destination = 184 | args._.length > 1 ? path.resolve(process.cwd(), `${args._[1]}`) : ''; 185 | const _destinationExtname = path.extname(_destination).toLowerCase(); 186 | 187 | let _destinationPaths = []; 188 | if ('' === _destination) { 189 | _destinationPaths = _sourcePaths.map( 190 | extensionCorrector(_defaultExtension, _requiredExtension) 191 | ); 192 | } else { 193 | if ('' !== _destinationExtname) { 194 | if ('.sass' === _destinationExtname || '.scss' === _destinationExtname) { 195 | _requiredExtension = _destinationExtname; 196 | _destinationPaths = [_destination]; 197 | } else { 198 | _destinationPaths = [removePathExtension(_destination)].map( 199 | extensionCorrector(_defaultExtension, _requiredExtension) 200 | ); 201 | } 202 | } else { 203 | _destinationPaths = _sourcePaths 204 | .map(basenameExtractor) 205 | .map(extensionCorrector(_defaultExtension, _requiredExtension)) 206 | .map(dirnameSetter(_destination)); 207 | } 208 | } 209 | const _mergeSourceFiles = 210 | _sourcePaths.length > 1 && _sourcePaths.length !== _destinationPaths.length; 211 | 212 | return { 213 | source: { 214 | paths: _sourcePaths 215 | }, 216 | destination: { 217 | paths: _destinationPaths 218 | }, 219 | options: { 220 | prefix: 'p' in args ? args.p : '', 221 | suffix: 's' in args ? args.s : ';', 222 | emptyString: 'es' in args && 'sq' === args.es ? "''" : '""', 223 | indentationText: 'tt' in args ? args.tt : ' ', 224 | indentationSize: 'tn' in args ? args.tn : 1, 225 | noUnderscore: !('underscore' in args), 226 | format: 227 | '' === _requiredExtension ? _defaultExtension : _requiredExtension, 228 | mergeSourceFiles: _mergeSourceFiles, 229 | mergeSassObjects: _mergeSourceFiles && 'mo' in args, 230 | keys: 'k' in args ? ['auto', 'sq', 'dq'].indexOf(args.k) > -1 ? args.k : 'auto' : 'auto', 231 | values: 'v' in args ? ['auto', 'sq', 'dq'].indexOf(args.v) > -1 ? args.v : 'auto' : 'auto', 232 | stringKeys: 'sk' in args && isString(args.sk) ? args.sk : 'family,font-family,fontfamily,font-stack,fontstack,font-face,fontface', 233 | flattenKeys: 'fk' in args, 234 | flattenedKeyCase: 'fkc' in args ? ['kebab', 'camel'].indexOf(args.fkc) > -1 ? args.fkc : 'kebab' : 'kebab' 235 | } 236 | }; 237 | } 238 | 239 | /** 240 | * The 'json-to-scss' main function in charge of parsing arguments and, if possible, 241 | * executing the file conversion. 242 | */ 243 | function main() { 244 | console.log(banner(packageJson.name, packageJson.version)); 245 | if (hasArgs(args)) { 246 | if ('h' in args) { 247 | console.log(usage(packageJson.name)); 248 | } else { 249 | const _nargs = normalizeArgs(args); 250 | if (_nargs.source.paths.length) { 251 | jsJsonFilesToSassScssFiles( 252 | _nargs.source.paths, 253 | _nargs.destination.paths, 254 | _nargs.options.prefix, 255 | _nargs.options.suffix, 256 | _nargs.options.format, 257 | _nargs.options.indentationText, 258 | _nargs.options.indentationSize, 259 | _nargs.options.emptyString, 260 | _nargs.options.noUnderscore, 261 | _nargs.options.mergeSourceFiles, 262 | _nargs.options.mergeSassObjects, 263 | _nargs.options.keys, 264 | _nargs.options.values, 265 | _nargs.options.stringKeys, 266 | _nargs.options.flattenKeys, 267 | _nargs.options.flattenedKeyCase 268 | ); 269 | } else { 270 | console.log( 271 | `Hmmm strange... ${chalk.red( 272 | args._[0] 273 | )} cannot be found. Could there be a small mistake in the source path?` 274 | ); 275 | } 276 | } 277 | } else { 278 | console.log(usage(packageJson.name)); 279 | } 280 | } 281 | 282 | /** 283 | * Execute the main module function. 284 | */ 285 | main(); 286 | -------------------------------------------------------------------------------- /lib/jsJsonFilesToSassScssFiles.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /** 3 | * @module json-to-scss/lib/jsJsonFilesToSassScssFiles 4 | * @author Renaud Lapoële 5 | */ 6 | 7 | /** 8 | * Modules imports/dependencies. 9 | */ 10 | const fs = require('fs'); 11 | const path = require('path'); 12 | const terminal = require('terminal-overwrite'); 13 | const emoji = require('node-emoji'); 14 | const chalk = require('chalk'); 15 | const pathBasename = require('../lib/utils/path/basename'); 16 | const hasExtension = require('../lib/utils/path/hasExtension'); 17 | const createPathDirectories = require('../lib/utils/path/createPathDirectories'); 18 | const isBoolean = require('../lib/utils/inspection/isBoolean'); 19 | const isPositiveInteger = require('../lib/utils/inspection/isPositiveInteger'); 20 | const isString = require('../lib/utils/inspection/isString'); 21 | const jsValueToSassString = require('../lib/jsValueToSassString'); 22 | const flattenObject = require('../lib/utils/transformation/flattenObject'); 23 | 24 | /** 25 | * @function jsJsonFilesToSassScssFiles 26 | * @param {array} sourceFilepaths - an array of "resolved" source file paths. 27 | * @param {array} destinationFilepaths - an array of destination file paths; it length must be equal to the source file path array's length. 28 | * @param {string} prefix 29 | * @param {string} suffix 30 | * @param {string} format 31 | * @param {string} indentationText 32 | * @param {number} indentationSize 33 | * @param {string} emptyString - '' or "". 34 | * @param {boolean} noUnderscore 35 | * @param {boolean} mergeSourceFiles 36 | * @param {boolean} mergeSassObjects 37 | * @param {string} keyFormat - 'auto', 'sq' or 'dq' 38 | * @param {string} valueFormat - 'auto', 'sq' or 'dq' 39 | * @param {string} stringKeys 40 | * @param {boolean} flattenKeys 41 | * @param {string} flattenedKeyCase 42 | * @description Converts node js (.js files exporting an object...) & json files to sass or scss files. 43 | */ 44 | function jsJsonFilesToSassScssFiles( 45 | sourceFilepaths, 46 | destinationFilepaths, 47 | prefix, 48 | suffix, 49 | format, 50 | indentationText, 51 | indentationSize, 52 | emptyString, 53 | noUnderscore, 54 | mergeSourceFiles, 55 | mergeSassObjects, 56 | keyFormat, 57 | valueFormat, 58 | stringKeys, 59 | flattenKeys, 60 | flattenedKeyCase 61 | ) { 62 | let mergedSassStrings = ''; 63 | 64 | /** 65 | * @private 66 | * @function _convertFile 67 | * @param {string} filepath 68 | * @param {number} fileindex 69 | * @description loads, converts and potentially (no merge context) saves converted content into a file. 70 | */ 71 | function _convertFile(filepath, fileindex) { 72 | terminal(`${emoji.get('gear')} ${chalk.blue(filepath)}:`); 73 | const _filepathExtension = path.extname(filepath); 74 | 75 | // skip any files if extension is <> than ".js" or ".json". 76 | if (-1 === ['.js', '.json'].indexOf(_filepathExtension)) { 77 | terminal( 78 | `${emoji.get('x')} ${chalk.blue( 79 | filepath 80 | )}: unsupported file format: "${_filepathExtension}". ${chalk.yellow( 81 | 'File skipped!' 82 | )}\n` 83 | ); 84 | } else { 85 | let _prefix = prefix; 86 | let _suffix = suffix; 87 | let _indentationText = indentationText; 88 | let _indentationSize = indentationSize; 89 | let _emptyString = emptyString; 90 | let _noUnderscore = noUnderscore; 91 | let _destinationFilepath = mergeSourceFiles 92 | ? destinationFilepaths[0] 93 | : destinationFilepaths[fileindex]; 94 | let _destinationFilename = ''; 95 | let _keyFormat = keyFormat; 96 | let _valueFormat = valueFormat; 97 | let _stringKeys = stringKeys.split(',').map((v) => v.trim().toUpperCase()); 98 | let _flattenKeys = flattenKeys; 99 | let _jsObject = {}; 100 | let _errorFlag = false; 101 | 102 | // let's try to "import" the source file. 103 | try { 104 | _jsObject = require(filepath); 105 | } catch (error) { 106 | // Oops - something went wrong (most likely a JSON format error). 107 | terminal( 108 | `${emoji.get('-1')} ${chalk.blue( 109 | filepath 110 | )}: error(s) found while parsing; content skipped. ${chalk.yellow( 111 | 'File skipped!' 112 | )}\n` 113 | ); 114 | _errorFlag = true; 115 | } 116 | 117 | // verify that no error occurred previously and if it did, then skip the 118 | // rest of the process for the concerned file. 119 | if (!_errorFlag) { 120 | // let's check if the source content has a local config (option set) 121 | // defined. 122 | if ('_jsonToScss' in _jsObject) { 123 | const { _jsonToScss } = _jsObject; 124 | 125 | // if yes, then use its content: 126 | // get prefix if there is one defined. 127 | if ('prefix' in _jsonToScss && isString(_jsonToScss.prefix)) { 128 | _prefix = _jsObject._jsonToScss.prefix; 129 | } 130 | 131 | // get suffix if one is specified. 132 | if ('suffix' in _jsonToScss && isString(_jsonToScss.suffix)) { 133 | _suffix = _jsObject._jsonToScss.suffix; 134 | } 135 | 136 | // if no merge && indentationText then grab it. 137 | if ( 138 | !mergeSourceFiles && 139 | 'indentationText' in _jsonToScss && 140 | isString(_jsonToScss.indentationText) 141 | ) { 142 | _indentationText = _jsObject._jsonToScss.indentationText; 143 | } 144 | 145 | // if no merge && indentationSize then grab it. 146 | if ( 147 | !mergeSourceFiles && 148 | 'indentationSize' in _jsonToScss && 149 | isPositiveInteger(_jsonToScss.indentationSize) 150 | ) { 151 | _indentationSize = _jsObject._jsonToScss.indentationSize; 152 | } 153 | 154 | // if no merge && emptyString then get it. 155 | if ( 156 | !mergeSourceFiles && 157 | 'emptyString' in _jsonToScss && 158 | ('""' === _jsonToScss.emptyString || 159 | "''" === _jsonToScss.emptyString) 160 | ) { 161 | _emptyString = _jsObject._jsonToScss.emptyString; 162 | } 163 | 164 | // if noUnderscore, then use it. 165 | if ( 166 | 'noUnderscore' in _jsonToScss && 167 | isBoolean(_jsonToScss.noUnderscore) 168 | ) { 169 | _noUnderscore = _jsObject._jsonToScss.noUnderscore; 170 | } 171 | 172 | // if sassVariableName, then grab it and reset prefix if no file merge. 173 | if ( 174 | !mergeSourceFiles && 175 | 'sassVariableName' in _jsonToScss && 176 | isString(_jsonToScss.sassVariableName) && 177 | _jsonToScss.sassVariableName.length 178 | ) { 179 | _prefix = _flattenKeys 180 | ? `$${_jsonToScss.sassVariableName}` 181 | : `$${_jsonToScss.sassVariableName}: `; 182 | } 183 | 184 | 185 | // if no merge and good filename :) then prepare it and update the 186 | // "copy" of the original destination file path. 187 | if ( 188 | !mergeSourceFiles && 189 | 'filename' in _jsonToScss && 190 | isString(_jsonToScss.filename) && 191 | _jsonToScss.filename.length 192 | ) { 193 | _destinationFilename = hasExtension(_jsonToScss.filename) 194 | ? pathBasename(_jsonToScss.filename) 195 | : _jsObject._jsonToScss.filename; 196 | 197 | if (_destinationFilename.length) { 198 | _destinationFilepath = `${path.join( 199 | path.dirname(_destinationFilepath), 200 | _destinationFilename 201 | )}${path.extname(_destinationFilepath)}`; 202 | } 203 | } 204 | 205 | // check if sass map keys formatting (wrapping in quotes) is required. 206 | if ( 207 | 'keyFormat' in _jsonToScss && 208 | -1 !== ['auto','sq','dq'].indexOf(_jsonToScss.keyFormat) 209 | ) { 210 | _keyFormat = _jsonToScss.keyFormat; 211 | } 212 | 213 | // check if sass map values formatting (wrapping in quotes) is required. 214 | if ( 215 | 'valueFormat' in _jsonToScss && 216 | -1 !== ['auto','sq','dq'].indexOf(_jsonToScss.valueFormat) 217 | ) { 218 | _valueFormat = _jsonToScss.valueFormat; 219 | } 220 | 221 | // check if some props must be treated as string forcefully. 222 | if ( 223 | 'stringKeys' in _jsonToScss && 224 | isString(_jsonToScss.stringKeys)) { 225 | _stringKeys = _jsonToScss.stringKeys.split(',').map((v) => v.trim().toUpperCase()) 226 | } 227 | 228 | // let us remove the local config from the content. 229 | delete _jsObject._jsonToScss; 230 | } 231 | 232 | // if no prefix is specified then set one using: 233 | // - $ if flattenKeys option is on 234 | // else 235 | // - the source filename (as sass variable) if we are merging several 236 | // sources into one destination. 237 | // or at last 238 | // - the destination filename otherwise. 239 | if ('' === _prefix) { 240 | _prefix = 241 | _flattenKeys 242 | ? '$' 243 | : (mergeSourceFiles && mergeSassObjects) || !mergeSourceFiles 244 | ? `$${pathBasename(_destinationFilepath)}: ` 245 | : `$${pathBasename(filepath)}: `; 246 | } 247 | 248 | // if sass format is required then disable the sass content indentation 249 | // and if the suffix starts and/or ends with ";" then remove it. 250 | // this is currently kept basic as it could become very complex very 251 | // fast otherwise. 252 | if ('.sass' === format) { 253 | _indentationSize = 0; 254 | _suffix = _suffix.trim(); 255 | 256 | if (_suffix.startsWith(';')) { 257 | _suffix = _suffix.slice(1); 258 | } 259 | 260 | if (_suffix.endsWith(';')) { 261 | _suffix = _suffix.slice(0, -1); 262 | } 263 | } 264 | 265 | // let's see if the --no-underscore option was set and if so, then while 266 | // the prefix starts with "$_" let's remove the "_". 267 | if (_noUnderscore) { 268 | while (_prefix.startsWith('$_')) { 269 | _prefix = `$${_prefix.slice(2)}`; 270 | } 271 | } 272 | 273 | let _sassString = ''; 274 | 275 | if (_flattenKeys && !!Object.keys(_jsObject).length) { 276 | // convert all nested objects into flat objects and convert the results 277 | // into a series of sass/scss variables. 278 | let _flattenObject = flattenObject(_jsObject, flattenedKeyCase); 279 | 280 | let count = 0; 281 | for (let _aJsObjectKey in _flattenObject) { 282 | let _newSassString = `${jsValueToSassString( 283 | _flattenObject[_aJsObjectKey], 284 | _indentationText, 285 | _indentationSize, 286 | _emptyString, 287 | _keyFormat, 288 | _valueFormat, 289 | _stringKeys 290 | )}`; 291 | 292 | _sassString = 0 === count 293 | ? `${_prefix}${_aJsObjectKey}: ${_newSassString}${_suffix}` 294 | : `${_sassString}\n${_prefix}${_aJsObjectKey}: ${_newSassString}${_suffix}` 295 | 296 | count++; 297 | } 298 | //if (0 < _sassString.length) { 299 | // _sassString = `${_sassString}\n`; 300 | //} 301 | } 302 | else { 303 | // convert the js object into a sass string. 304 | _sassString = `${jsValueToSassString( 305 | _jsObject, 306 | _indentationText, 307 | _indentationSize, 308 | _emptyString, 309 | _keyFormat, 310 | _valueFormat, 311 | _stringKeys 312 | )}`; 313 | } 314 | 315 | terminal( 316 | `${emoji.get('hourglass_flowing_sand')} ${chalk.blue( 317 | filepath 318 | )}: content converted.` 319 | ); 320 | if (0 === Object.keys(_jsObject).length) { 321 | terminal( 322 | `${emoji.get('-1')} ${chalk.blue( 323 | filepath 324 | )}: no convertible content found. ${chalk.yellow( 325 | 'File skipped!' 326 | )}\n` 327 | ); 328 | } 329 | else { 330 | if (!mergeSourceFiles) { 331 | try { 332 | // ensure that the destination directory(ies) exist and if not 333 | // create it. 334 | createPathDirectories(_destinationFilepath); 335 | 336 | // let's write the file. 337 | // Ignore prefix and suffix if js object has been flatten. 338 | fs.writeFileSync( 339 | _destinationFilepath, 340 | _flattenKeys ? `${_sassString}\n` : `${_prefix}${_sassString}${_suffix}\n` 341 | ); 342 | 343 | terminal( 344 | `${emoji.get('hourglass')} ${chalk.blue(filepath)}: content converted. ${chalk.green('File created!')}\n` 345 | ); 346 | terminal( 347 | ` ${emoji.get('+1')} ${chalk.green(_destinationFilepath)}\n` 348 | ); 349 | } catch (error) { 350 | terminal( 351 | `${emoji.get('-1')} ${chalk.blue( 352 | filepath 353 | )}: Oops! Something went wrong when trying to write the converted file. ${chalk.yellow( 354 | 'File skipped!' 355 | )}\n` 356 | ); 357 | } 358 | } 359 | else { 360 | if (mergeSassObjects && !_flattenKeys) { 361 | // Objects coming from several files must be merged but not flattened. 362 | 363 | // First file to be processed? 364 | if (0 === fileindex) { 365 | // first file of many and sass value is map? 366 | if ( 367 | fileindex < sourceFilepaths.length - 1 && 368 | _sassString.endsWith('\n)') 369 | ) { 370 | _sassString = _sassString.slice(0, -2); 371 | } 372 | mergedSassStrings = `${_prefix}${_sassString}`; 373 | terminal( 374 | `${emoji.get('hourglass')} ${chalk.blue( 375 | filepath 376 | )}: content converted.\n` 377 | ); 378 | } 379 | // Not the first file? 380 | if ( 381 | 0 < fileindex && 382 | fileindex <= sourceFilepaths.length - 1 383 | ) { 384 | // is sass value map? 385 | if (_sassString.startsWith('(\n')) { 386 | _sassString = _sassString.slice(2); 387 | } 388 | 389 | // is sass value map and file is not last? 390 | if ( 391 | fileindex < sourceFilepaths.length - 1 && 392 | _sassString.endsWith('\n)') 393 | ) { 394 | _sassString = _sassString.slice(0, -2); 395 | } 396 | 397 | mergedSassStrings = `${mergedSassStrings},\n${_sassString}`; 398 | if (fileindex < sourceFilepaths.length - 1) { 399 | terminal( 400 | `${emoji.get('hourglass')} ${chalk.blue( 401 | filepath 402 | )}: content converted & merged.\n` 403 | ); 404 | } else { 405 | } 406 | } 407 | // Is file being processed the last? 408 | if (fileindex === sourceFilepaths.length - 1) { 409 | mergedSassStrings = `${mergedSassStrings}${_suffix}\n`; 410 | try { 411 | // file being processed is the last one so now we can save the 412 | // "merged" content. 413 | createPathDirectories(_destinationFilepath); 414 | 415 | // let's write the file. 416 | fs.writeFileSync(_destinationFilepath, mergedSassStrings); 417 | 418 | terminal( 419 | `${emoji.get('hourglass')} ${chalk.blue( 420 | filepath 421 | )}: content converted & merged. ${chalk.green( 422 | 'File created!' 423 | )}\n` 424 | ); 425 | terminal( 426 | ` ${emoji.get('+1')} ${chalk.green( 427 | _destinationFilepath 428 | )}\n` 429 | ); 430 | } catch (error) { 431 | terminal( 432 | `${emoji.get('-1')} ${chalk.blue( 433 | filepath 434 | )}: Oops! Something went wrong when trying to write the converted content file. ${chalk.yellow( 435 | 'File skipped!' 436 | )}\n` 437 | ); 438 | } 439 | } 440 | } 441 | else { 442 | // merge scenario so append new converted content to the existing 443 | // one. 444 | mergedSassStrings = _flattenKeys 445 | ? `${mergedSassStrings}${_sassString}\n` 446 | : `${mergedSassStrings}${_prefix}${_sassString}${_suffix}\n`; 447 | 448 | // is the source file being processed the last one? 449 | // No, then let the user know that the source content was 450 | // converted but nothing more/else. 451 | if (fileindex < sourceFilepaths.length - 1) { 452 | terminal( 453 | `${emoji.get('hourglass')} ${chalk.blue( 454 | filepath 455 | )}: content converted.\n` 456 | ); 457 | } 458 | else { 459 | try { 460 | // file being processed is the last one so now we can save the 461 | // "merged" content. 462 | createPathDirectories(_destinationFilepath); 463 | 464 | // let's write the file. 465 | fs.writeFileSync(_destinationFilepath, mergedSassStrings); 466 | 467 | terminal( 468 | `${emoji.get('hourglass')} ${chalk.blue( 469 | filepath 470 | )}: content converted. ${chalk.green('File created!')}\n` 471 | ); 472 | terminal( 473 | ` ${emoji.get('+1')} ${chalk.green( 474 | _destinationFilepath 475 | )}\n` 476 | ); 477 | } catch (error) { 478 | terminal( 479 | `${emoji.get('-1')} ${chalk.blue( 480 | filepath 481 | )}: Oops! Something went wrong when trying to write the converted content file. ${chalk.yellow( 482 | 'File skipped!' 483 | )}\n` 484 | ); 485 | } 486 | } 487 | } 488 | } 489 | } 490 | } 491 | } 492 | } 493 | 494 | // iterate through each input source file and perform conversion when possible. 495 | sourceFilepaths.forEach(_convertFile); 496 | } 497 | 498 | /** 499 | * Module exports. 500 | */ 501 | module.exports = jsJsonFilesToSassScssFiles; 502 | -------------------------------------------------------------------------------- /lib/jsValueToSassString.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /** 3 | * @module json-to-scss/lib/jsValueToSassString 4 | * @author Renaud Lapoële 5 | */ 6 | 7 | /** 8 | * Module imports/dependencies. 9 | */ 10 | const isPlainObject = require('lodash.isplainobject'); 11 | const isNull = require('./utils/inspection/isNull'); 12 | const isUndefined = require('./utils/inspection/isUndefined'); 13 | const { isArray } = Array; 14 | 15 | /** 16 | * Module constants (or alike) 17 | */ 18 | const FN_DEFAULT_INDENTATION_TEXT = ' '; 19 | const FN_DEFAULT_INDENTATION_SIZE = 1; 20 | const FN_DEFAULT_EMPTY_STRING = '""'; 21 | const FN_FORMAT_AUTO = 'auto'; 22 | const FN_DEFAULT_KEY_FORMAT = FN_FORMAT_AUTO; 23 | const FN_DEFAULT_VALUE_FORMAT = FN_FORMAT_AUTO; 24 | const FN_DEFAULT_STRING_KEYS = []; 25 | const FN_FORMAT_SINGLE_QUOTED = 'sq'; 26 | 27 | /** 28 | * @function jsValueToSassString 29 | * @param {*} value - the js value to be converted into a string compatible with sass/scss syntax. 30 | * @param {string} indentationText - the string to be used as indentation text. 31 | * @param {number} indentationSize - the number of time the indentation text must be repeated per indentation level. 32 | * @param {string} emptyString 33 | * @param {string} keyFormat - an indicator telling how sass map keys must be formatted. 34 | * @param {string} valueFormat - an indicator telling how sass map values must be formatted. 35 | * @param {array} stringKeys - an array containing property names for which values must be forcefully "quoted" (e.g: font-family). 36 | * @returns {string} string sass representation of the value. 37 | * @description Converts a javascript value into a string compatible with sass/scss syntax. 38 | */ 39 | function jsValueToSassString( 40 | value, 41 | indentationText = FN_DEFAULT_INDENTATION_TEXT, 42 | indentationSize = FN_DEFAULT_INDENTATION_SIZE, 43 | emptyString = FN_DEFAULT_EMPTY_STRING, 44 | keyFormat = FN_DEFAULT_KEY_FORMAT, 45 | valueFormat = FN_DEFAULT_VALUE_FORMAT, 46 | stringKeys= FN_DEFAULT_STRING_KEYS 47 | ) { 48 | 49 | /** 50 | * @private 51 | * @function _formatString 52 | * @param {string} string - the string to be formatted. 53 | * @param {string} format - the format to be used. 54 | * @returns {string} 55 | */ 56 | function _formatString(string, format) { 57 | if ('string' !== typeof string) { 58 | return string; 59 | } 60 | if (!string) { 61 | return format === FN_FORMAT_AUTO ? 62 | emptyString : 63 | format === FN_FORMAT_SINGLE_QUOTED ? 64 | "''": 65 | '""'; 66 | } 67 | else { 68 | let sqRegExp = /'/g; 69 | let dqRegExp = /"/g; 70 | let _sqString = string.replace(sqRegExp, '"'); 71 | let _dqString = string.replace(dqRegExp, "'"); 72 | return format === FN_FORMAT_AUTO ? 73 | string : 74 | format === FN_FORMAT_SINGLE_QUOTED ? 75 | `'${_sqString}'` : 76 | `"${_dqString}"`; 77 | } 78 | } 79 | 80 | function _quoteIfStringKey(propertyName, value, quoteFormat) { 81 | if ('' !== propertyName) { 82 | if (stringKeys.includes(propertyName.toUpperCase())) { 83 | return quoteFormat === FN_FORMAT_SINGLE_QUOTED ? `'${value}'` : `"${value}"`; 84 | } 85 | } 86 | return value; 87 | } 88 | 89 | 90 | // computed flag. 91 | const mustIndent = '' !== indentationText && 0 !== indentationSize; 92 | 93 | /** 94 | * @private 95 | * @function _process 96 | * @param {*|string} propertyName - the property for which the value is being processed. 97 | * @param {*} value - the js value to be converted into a sass string. 98 | * @param {number} indentationLevel - a positive integer reflecting the desired level of indentation. 99 | * @returns {*|string} string sass representation of the value. 100 | * @description actual implementation of the `jsValueToSassString` function. 101 | */ 102 | function _process(propertyName, value, indentationLevel) { 103 | const _switch = { 104 | boolean: () => _formatString(value.toString(), valueFormat), 105 | number: () => _formatString(value.toString(), valueFormat), 106 | string: () => _formatString(value, valueFormat), 107 | object: () => { 108 | if (isPlainObject(value)) { 109 | let _jsObj = value; 110 | let _sassKeyValPairs = Object.keys(_jsObj).reduce( 111 | (result, key) => { 112 | let _jsVal = _jsObj[key]; 113 | let _propName = isPlainObject(_jsVal) ? '' : key; 114 | let _sassVal = _process(_propName, _jsVal, indentationLevel + 1); 115 | if (!isUndefined(_sassVal)) { 116 | result.push(`${_formatString(key, keyFormat)}: ${_quoteIfStringKey(_propName, _sassVal, valueFormat)}`); 117 | } 118 | return result; 119 | }, 120 | [] 121 | ); 122 | 123 | let _result = ''; 124 | if (mustIndent) { 125 | let _indentIn = indentationText.repeat( 126 | indentationSize * (indentationLevel + 1) 127 | ); 128 | let _indentOut = indentationText.repeat( 129 | indentationSize * indentationLevel 130 | ); 131 | _result = `(\n${_indentIn + 132 | _sassKeyValPairs.join(`,\n${_indentIn}`)}\n${_indentOut})`; 133 | } else 134 | _result = `(${_sassKeyValPairs.join(', ')})`; 135 | 136 | return _result; 137 | } 138 | else if (isArray(value)) { 139 | let _sassVals = value.reduce( 140 | (result, v) => { 141 | if (!isUndefined(v)) 142 | result.push(_process('', v, indentationLevel)); 143 | return result; 144 | }, 145 | [] 146 | ); 147 | return `(${_sassVals.join(', ')})`; 148 | } 149 | else if (isNull(value)) { 150 | return 'null'; 151 | } 152 | return _formatString(value.toString(), valueFormat); 153 | }, 154 | default: () => {} 155 | }; 156 | return (_switch[typeof value] || _switch['default'])(); 157 | } 158 | 159 | return _process('', value, 0); 160 | } 161 | 162 | /** 163 | * Module exports. 164 | */ 165 | module.exports = jsValueToSassString; 166 | -------------------------------------------------------------------------------- /lib/utils/inspection/isBoolean.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /** 3 | * @module lib/utils/inspection/isBoolean 4 | * @author Renaud Lapoële 5 | */ 6 | 7 | /** 8 | * @function isBoolean 9 | * @param {*} value - the value to check. 10 | * @returns {boolean} true if the given value is a boolean, false otherwise. 11 | * @description A Function to verify whether a value is a boolean or not. 12 | */ 13 | function isBoolean(value) { 14 | return typeof value === 'boolean'; 15 | } 16 | 17 | /** 18 | * Module exports. 19 | */ 20 | module.exports = isBoolean; 21 | -------------------------------------------------------------------------------- /lib/utils/inspection/isNull.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /** 3 | * @module lib/utils/inspection/isNull 4 | * @author Renaud Lapoële 5 | */ 6 | 7 | /** 8 | * @function isNull 9 | * @param {*} value 10 | * @returns {boolean} true if the value is null, false otherwise 11 | * @description A function to check whether a given value is null or not. 12 | */ 13 | function isNull(value) { 14 | return value === null; 15 | } 16 | 17 | /** 18 | * Module exports. 19 | */ 20 | module.exports = isNull; 21 | -------------------------------------------------------------------------------- /lib/utils/inspection/isPositiveInteger.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /** 3 | * @module lib/utils/inspection/isPositiveInteger 4 | * @author Renaud Lapoële 5 | */ 6 | 7 | /** 8 | * Module imports. 9 | */ 10 | const isInteger = require('lodash.isinteger'); 11 | 12 | /** 13 | * @function isPositiveInteger 14 | * @param {*} value - the value to check. 15 | * @returns {boolean} true if the value passed as argument is an integer and is positive. 16 | * @description A function to check whether a given value is a positive integer or not. 17 | */ 18 | function isPositiveInteger(value) { 19 | return isInteger(value) && 0 <= value; 20 | } 21 | 22 | /** 23 | * Module exports. 24 | */ 25 | module.exports = isPositiveInteger; 26 | -------------------------------------------------------------------------------- /lib/utils/inspection/isString.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /** 3 | * @module lib/utils/inspection/isString 4 | * @author Renaud Lapoële 5 | */ 6 | 7 | /** 8 | * @function isString 9 | * @param {*} value - the value to check. 10 | * @returns {boolean} true if the given value is a string, false otherwise. 11 | * @description A Function to verify whether a value is a string or not. 12 | */ 13 | function isString(value) { 14 | return typeof value === 'string'; 15 | } 16 | 17 | /** 18 | * Module exports. 19 | */ 20 | module.exports = isString; 21 | -------------------------------------------------------------------------------- /lib/utils/inspection/isUndefined.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /** 3 | * @module lib/utils/inspection/isUndefined 4 | * @author Renaud Lapoële 5 | */ 6 | 7 | /** 8 | * @function isUndefined 9 | * @param {*} value - the value to check. 10 | * @returns {boolean} - true if the typeof value is 'undefined' and false otherwise. 11 | * @description A Function to verify whether a value is undefined or not. 12 | */ 13 | function isUndefined(value) { 14 | return typeof value === 'undefined'; 15 | } 16 | 17 | /** 18 | * Module exports. 19 | */ 20 | module.exports = isUndefined; 21 | -------------------------------------------------------------------------------- /lib/utils/path/basename.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /** 3 | * @module lib/utils/path/basename 4 | * @author Renaud Lapoële 5 | */ 6 | 7 | /** 8 | * Module imports/dependencies. 9 | */ 10 | const path = require('path'); 11 | 12 | /** 13 | * @function basename 14 | * @param {string} filePath 15 | * @returns {string} the basename of the file path minus its extension(s). 16 | * @description Extract a file name from a file path. 17 | */ 18 | function basename(filePath) { 19 | const _filePathExtension = path.extname(filePath); 20 | const _filePathBasename = path.basename(filePath, _filePathExtension); 21 | return '' !== _filePathExtension 22 | ? basename(_filePathBasename) 23 | : _filePathBasename; 24 | } 25 | 26 | /** 27 | * Module exports. 28 | */ 29 | module.exports = basename; 30 | -------------------------------------------------------------------------------- /lib/utils/path/createPathDirectories.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /** 3 | * @module lib/utils/path/createPathDirectories 4 | * @author Renaud Lapoële 5 | */ 6 | 7 | /** 8 | * Module imports/dependencies 9 | */ 10 | const path = require('path'); 11 | const fs = require('fs'); 12 | 13 | /** 14 | * @function createPathDirectories 15 | * @param {string} filepath 16 | * @returns {void|boolean} true if the directory(ies) already exist nothing otherwise. 17 | * @description Creates missing path directories. 18 | */ 19 | function createPathDirectories(filepath) { 20 | const dirname = path.dirname(filepath); 21 | if (fs.existsSync(dirname)) { 22 | return true; 23 | } 24 | createPathDirectories(dirname); 25 | fs.mkdirSync(dirname); 26 | } 27 | 28 | /** 29 | * Module exports. 30 | */ 31 | module.exports = createPathDirectories; 32 | -------------------------------------------------------------------------------- /lib/utils/path/hasExtension.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /** 3 | * @module lib/utils/path/hasExtension 4 | * @author Renaud Lapoële 5 | */ 6 | 7 | /** 8 | * Module imports/dependencies. 9 | */ 10 | const path = require('path'); 11 | 12 | /** 13 | * @function hasExtension 14 | * @param {string} filePath - the file path for which one wants to know if it has an extension. 15 | * @returns {boolean} true if the file path given as argument has an extension (.xxx), false otherwise. 16 | * @description Returns true if the filePath has an extension. 17 | */ 18 | function hasExtension(filePath) { 19 | return '' !== path.extname(filePath); 20 | } 21 | 22 | /** 23 | * Module exports. 24 | */ 25 | module.exports = hasExtension; 26 | -------------------------------------------------------------------------------- /lib/utils/path/removeExtension.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /** 3 | * @module lib/utils/path/removeExtension 4 | * @author Renaud Lapoële 5 | */ 6 | 7 | /** 8 | * Module imports/dependencies 9 | */ 10 | const path = require('path'); 11 | 12 | /** 13 | * @function removeExtension 14 | * @param {string} filePath - path from which file extension(s) must be removed. 15 | * @param {boolean} removeExtensionRecursively - indicates if, in case file path has multiple extensions, extensions must all be removed. 16 | * @returns {string} the file path with less or without any file extension. 17 | * @description Removes one and/or all file extensions from a file path. 18 | */ 19 | function removeExtension(filePath, removeExtensionRecursively = false) { 20 | const _extname = path.extname(filePath); 21 | if ('' === _extname) { 22 | return filePath; 23 | } else { 24 | const _filePath = path.join( 25 | path.dirname(filePath), 26 | path.basename(filePath, _extname) 27 | ); 28 | if (removeExtensionRecursively) { 29 | if ('' === path.extname(_filePath)) { 30 | return _filePath; 31 | } else { 32 | return removeExtension(_filePath); 33 | } 34 | } else { 35 | return _filePath; 36 | } 37 | } 38 | } 39 | 40 | /** 41 | * Module exports. 42 | */ 43 | module.exports = removeExtension; 44 | -------------------------------------------------------------------------------- /lib/utils/transformation/flattenObject.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /** 3 | * @module lib/utils/transformation/flattenObject 4 | * @author Renaud Lapoële 5 | */ 6 | 7 | /** 8 | * @function flattenObject 9 | * @param {Object} obj 10 | * @param {string} keyCase 11 | * @returns {Object} a flattened object. 12 | * @description A function to flatten an object's nested objects/props. 13 | */ 14 | function flattenObject( 15 | obj, 16 | keyCase = 'kebab' 17 | ) { 18 | const _keyCase = -1 < ['kebab','camel'].indexOf(keyCase) ? keyCase : 'kebab'; 19 | const _obj = !!obj ? obj : {}; 20 | 21 | function _flatten(obj, currentKey = '', keyCase = 'kebab') { 22 | return Object.keys(obj).reduce( 23 | (acc, key) => { 24 | let newKey = ''; 25 | if ('camel' === keyCase) { 26 | newKey = '' === currentKey ? key : `${currentKey}${key.charAt(0).toUpperCase() + key.slice(1)}`; 27 | } 28 | else { 29 | newKey = '' === currentKey ? key : `${currentKey}-${key}`; 30 | } 31 | if ('object' === typeof obj[key] && !Array.isArray(obj[key])) 32 | acc = { ...acc, ..._flatten(obj[key], newKey, keyCase) }; 33 | else 34 | acc[newKey] = obj[key]; 35 | return acc; 36 | }, 37 | {} 38 | ); 39 | } 40 | return _flatten(_obj, '', _keyCase); 41 | } 42 | 43 | /** 44 | * Module exports. 45 | */ 46 | module.exports = flattenObject; 47 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "json-to-scss", 3 | "version": "1.6.2", 4 | "main": "lib/jsJsonFilesToSassScssFiles.js", 5 | "license": "MIT", 6 | "repository": { 7 | "type": "git", 8 | "url": "git+https://github.com/rlapoele/json-to-scss.git" 9 | }, 10 | "author": { 11 | "name": "Renaud Lapoële", 12 | "email": "rlapoele@gmail.com" 13 | }, 14 | "description": "A small utility to convert js & json file(s) to scss/sass file(s).", 15 | "keywords": [ 16 | "converter", 17 | "js", 18 | "json", 19 | "sass", 20 | "scss" 21 | ], 22 | "homepage": "https://github.com/rlapoele/json-to-scss#readme", 23 | "issues": "https://github.com/rlapoele/json-to-scss/issues", 24 | "dependencies": { 25 | "chalk": "^2.4.2", 26 | "glob": "^7.1.4", 27 | "lodash.isinteger": "^4.0.4", 28 | "lodash.isplainobject": "^4.0.6", 29 | "node-emoji": "^1.10.0", 30 | "terminal-overwrite": "^2.0.1", 31 | "yargs": "^13.3.2" 32 | }, 33 | "bin": { 34 | "json-to-scss": "bin/cli.js" 35 | }, 36 | "devDependencies": {} 37 | } 38 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | ansi-regex@^3.0.0: 6 | version "3.0.0" 7 | resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" 8 | integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= 9 | 10 | ansi-regex@^4.1.0: 11 | version "4.1.0" 12 | resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" 13 | integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== 14 | 15 | ansi-styles@^3.2.0, ansi-styles@^3.2.1: 16 | version "3.2.1" 17 | resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" 18 | integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== 19 | dependencies: 20 | color-convert "^1.9.0" 21 | 22 | balanced-match@^1.0.0: 23 | version "1.0.0" 24 | resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" 25 | integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= 26 | 27 | brace-expansion@^1.1.7: 28 | version "1.1.11" 29 | resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" 30 | integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== 31 | dependencies: 32 | balanced-match "^1.0.0" 33 | concat-map "0.0.1" 34 | 35 | camelcase@^5.0.0: 36 | version "5.3.1" 37 | resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" 38 | integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== 39 | 40 | chalk@^2.4.2: 41 | version "2.4.2" 42 | resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" 43 | integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== 44 | dependencies: 45 | ansi-styles "^3.2.1" 46 | escape-string-regexp "^1.0.5" 47 | supports-color "^5.3.0" 48 | 49 | cli-cursor@^2.0.0: 50 | version "2.1.0" 51 | resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" 52 | integrity sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU= 53 | dependencies: 54 | restore-cursor "^2.0.0" 55 | 56 | cliui@^5.0.0: 57 | version "5.0.0" 58 | resolved "https://registry.yarnpkg.com/cliui/-/cliui-5.0.0.tgz#deefcfdb2e800784aa34f46fa08e06851c7bbbc5" 59 | integrity sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA== 60 | dependencies: 61 | string-width "^3.1.0" 62 | strip-ansi "^5.2.0" 63 | wrap-ansi "^5.1.0" 64 | 65 | color-convert@^1.9.0: 66 | version "1.9.3" 67 | resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" 68 | integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== 69 | dependencies: 70 | color-name "1.1.3" 71 | 72 | color-name@1.1.3: 73 | version "1.1.3" 74 | resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" 75 | integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= 76 | 77 | concat-map@0.0.1: 78 | version "0.0.1" 79 | resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" 80 | integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= 81 | 82 | decamelize@^1.2.0: 83 | version "1.2.0" 84 | resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" 85 | integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= 86 | 87 | emoji-regex@^7.0.1: 88 | version "7.0.3" 89 | resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" 90 | integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== 91 | 92 | escape-string-regexp@^1.0.5: 93 | version "1.0.5" 94 | resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" 95 | integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= 96 | 97 | find-up@^3.0.0: 98 | version "3.0.0" 99 | resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" 100 | integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== 101 | dependencies: 102 | locate-path "^3.0.0" 103 | 104 | fs.realpath@^1.0.0: 105 | version "1.0.0" 106 | resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" 107 | integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= 108 | 109 | get-caller-file@^2.0.1: 110 | version "2.0.5" 111 | resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" 112 | integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== 113 | 114 | glob@^7.1.4: 115 | version "7.1.6" 116 | resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" 117 | integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== 118 | dependencies: 119 | fs.realpath "^1.0.0" 120 | inflight "^1.0.4" 121 | inherits "2" 122 | minimatch "^3.0.4" 123 | once "^1.3.0" 124 | path-is-absolute "^1.0.0" 125 | 126 | has-flag@^3.0.0: 127 | version "3.0.0" 128 | resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" 129 | integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= 130 | 131 | inflight@^1.0.4: 132 | version "1.0.6" 133 | resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" 134 | integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= 135 | dependencies: 136 | once "^1.3.0" 137 | wrappy "1" 138 | 139 | inherits@2: 140 | version "2.0.4" 141 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" 142 | integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== 143 | 144 | is-fullwidth-code-point@^2.0.0: 145 | version "2.0.0" 146 | resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" 147 | integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= 148 | 149 | locate-path@^3.0.0: 150 | version "3.0.0" 151 | resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" 152 | integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A== 153 | dependencies: 154 | p-locate "^3.0.0" 155 | path-exists "^3.0.0" 156 | 157 | lodash.isinteger@^4.0.4: 158 | version "4.0.4" 159 | resolved "https://registry.yarnpkg.com/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz#619c0af3d03f8b04c31f5882840b77b11cd68343" 160 | integrity sha1-YZwK89A/iwTDH1iChAt3sRzWg0M= 161 | 162 | lodash.isplainobject@^4.0.6: 163 | version "4.0.6" 164 | resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" 165 | integrity sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs= 166 | 167 | lodash.toarray@^4.4.0: 168 | version "4.4.0" 169 | resolved "https://registry.yarnpkg.com/lodash.toarray/-/lodash.toarray-4.4.0.tgz#24c4bfcd6b2fba38bfd0594db1179d8e9b656561" 170 | integrity sha1-JMS/zWsvuji/0FlNsRedjptlZWE= 171 | 172 | mimic-fn@^1.0.0: 173 | version "1.2.0" 174 | resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" 175 | integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ== 176 | 177 | minimatch@^3.0.4: 178 | version "3.0.4" 179 | resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" 180 | integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== 181 | dependencies: 182 | brace-expansion "^1.1.7" 183 | 184 | node-emoji@^1.10.0: 185 | version "1.10.0" 186 | resolved "https://registry.yarnpkg.com/node-emoji/-/node-emoji-1.10.0.tgz#8886abd25d9c7bb61802a658523d1f8d2a89b2da" 187 | integrity sha512-Yt3384If5H6BYGVHiHwTL+99OzJKHhgp82S8/dktEK73T26BazdgZ4JZh92xSVtGNJvz9UbXdNAc5hcrXV42vw== 188 | dependencies: 189 | lodash.toarray "^4.4.0" 190 | 191 | once@^1.3.0: 192 | version "1.4.0" 193 | resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" 194 | integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= 195 | dependencies: 196 | wrappy "1" 197 | 198 | onetime@^2.0.0: 199 | version "2.0.1" 200 | resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" 201 | integrity sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ= 202 | dependencies: 203 | mimic-fn "^1.0.0" 204 | 205 | p-limit@^2.0.0: 206 | version "2.3.0" 207 | resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" 208 | integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== 209 | dependencies: 210 | p-try "^2.0.0" 211 | 212 | p-locate@^3.0.0: 213 | version "3.0.0" 214 | resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" 215 | integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ== 216 | dependencies: 217 | p-limit "^2.0.0" 218 | 219 | p-try@^2.0.0: 220 | version "2.2.0" 221 | resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" 222 | integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== 223 | 224 | path-exists@^3.0.0: 225 | version "3.0.0" 226 | resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" 227 | integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU= 228 | 229 | path-is-absolute@^1.0.0: 230 | version "1.0.1" 231 | resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" 232 | integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= 233 | 234 | require-directory@^2.1.1: 235 | version "2.1.1" 236 | resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" 237 | integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= 238 | 239 | require-main-filename@^2.0.0: 240 | version "2.0.0" 241 | resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" 242 | integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== 243 | 244 | restore-cursor@^2.0.0: 245 | version "2.0.0" 246 | resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" 247 | integrity sha1-n37ih/gv0ybU/RYpI9YhKe7g368= 248 | dependencies: 249 | onetime "^2.0.0" 250 | signal-exit "^3.0.2" 251 | 252 | set-blocking@^2.0.0: 253 | version "2.0.0" 254 | resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" 255 | integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= 256 | 257 | signal-exit@^3.0.2: 258 | version "3.0.3" 259 | resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" 260 | integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== 261 | 262 | string-width@^2.0.0: 263 | version "2.1.1" 264 | resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" 265 | integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== 266 | dependencies: 267 | is-fullwidth-code-point "^2.0.0" 268 | strip-ansi "^4.0.0" 269 | 270 | string-width@^3.0.0, string-width@^3.1.0: 271 | version "3.1.0" 272 | resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" 273 | integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w== 274 | dependencies: 275 | emoji-regex "^7.0.1" 276 | is-fullwidth-code-point "^2.0.0" 277 | strip-ansi "^5.1.0" 278 | 279 | strip-ansi@^4.0.0: 280 | version "4.0.0" 281 | resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" 282 | integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8= 283 | dependencies: 284 | ansi-regex "^3.0.0" 285 | 286 | strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: 287 | version "5.2.0" 288 | resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" 289 | integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== 290 | dependencies: 291 | ansi-regex "^4.1.0" 292 | 293 | supports-color@^5.3.0: 294 | version "5.5.0" 295 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" 296 | integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== 297 | dependencies: 298 | has-flag "^3.0.0" 299 | 300 | terminal-overwrite@^2.0.1: 301 | version "2.0.1" 302 | resolved "https://registry.yarnpkg.com/terminal-overwrite/-/terminal-overwrite-2.0.1.tgz#c732aefeba38900667bf088b4f1b12edbee62841" 303 | integrity sha1-xzKu/ro4kAZnvwiLTxsS7b7mKEE= 304 | dependencies: 305 | cli-cursor "^2.0.0" 306 | string-width "^2.0.0" 307 | 308 | which-module@^2.0.0: 309 | version "2.0.0" 310 | resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" 311 | integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= 312 | 313 | wrap-ansi@^5.1.0: 314 | version "5.1.0" 315 | resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-5.1.0.tgz#1fd1f67235d5b6d0fee781056001bfb694c03b09" 316 | integrity sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q== 317 | dependencies: 318 | ansi-styles "^3.2.0" 319 | string-width "^3.0.0" 320 | strip-ansi "^5.0.0" 321 | 322 | wrappy@1: 323 | version "1.0.2" 324 | resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" 325 | integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= 326 | 327 | y18n@^4.0.0: 328 | version "4.0.0" 329 | resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b" 330 | integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w== 331 | 332 | yargs-parser@^13.1.2: 333 | version "13.1.2" 334 | resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.2.tgz#130f09702ebaeef2650d54ce6e3e5706f7a4fb38" 335 | integrity sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg== 336 | dependencies: 337 | camelcase "^5.0.0" 338 | decamelize "^1.2.0" 339 | 340 | yargs@^13.3.2: 341 | version "13.3.2" 342 | resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.2.tgz#ad7ffefec1aa59565ac915f82dccb38a9c31a2dd" 343 | integrity sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw== 344 | dependencies: 345 | cliui "^5.0.0" 346 | find-up "^3.0.0" 347 | get-caller-file "^2.0.1" 348 | require-directory "^2.1.1" 349 | require-main-filename "^2.0.0" 350 | set-blocking "^2.0.0" 351 | string-width "^3.0.0" 352 | which-module "^2.0.0" 353 | y18n "^4.0.0" 354 | yargs-parser "^13.1.2" 355 | --------------------------------------------------------------------------------