├── .gitignore ├── LICENSE ├── README.md ├── package-lock.json ├── package.json └── src └── index.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .idea 3 | bin 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Sergei Butko 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # react-native-image-resource-generator 2 | 3 | [![npm-version](https://img.shields.io/npm/v/react-native-image-resource-generator)](https://www.npmjs.com/package/react-native-image-resource-generator) 4 | [![npm-downloads](https://img.shields.io/npm/dt/react-native-image-resource-generator)](https://www.npmjs.com/package/react-native-image-resource-generator) 5 | 6 | [![GitHub Followers](https://img.shields.io/github/followers/svbutko?label=Follow%20%40svbutko&style=social)](https://github.com/svbutko) 7 | 8 | Generate code-friendly image URI source constants. 9 | To learn about use cases and what issues it solves check [this blog post](https://dev.to/svbutko/react-native-image-resource-generator-m14). 10 | 11 | ## Quick start 12 | 13 | ### Installation 14 | 15 | #### yarn 16 | 17 | ```sh 18 | yarn add -D react-native-image-resource-generator 19 | ``` 20 | 21 | #### npm 22 | 23 | ```sh 24 | npm install --save-dev react-native-image-resource-generator 25 | ``` 26 | 27 | ### Usage 28 | 29 | 1. Create a folder and put all of your images there (_sub-folders are supported too_). Example: 30 | ``` 31 | project 32 | │ package.json 33 | │ src 34 | │ 35 | └───resources 36 | │ │ fonts 37 | │ │ settings 38 | │ │ 39 | │ └───images 40 | │ │ arrow_down.png 41 | │ │ arrow_down@2x.png 42 | │ │ arrow_down@3x.png 43 | │ │ arrow_up.png 44 | │ │ ... 45 | ``` 46 | 2. Add script to your `package.json` scripts or type into terminal: 47 | * JavaScript: ```img-res-gen --dir=resources/images --out=src/common/ImageResources.js``` 48 | * TypeScript ```img-res-gen --dir=resources/images --out=src/common/ImageResources.g.ts --ts``` 49 | 3. The result of the previous command will create a file with static image URI sources, which will look something similar to this: 50 | ```typescript 51 | /* eslint:disable */ 52 | /* tslint:disable */ 53 | import {ImageURISource} from "react-native"; 54 | 55 | export class ImageResources { 56 | static readonly account: ImageURISource = require("../../resources/images/account.png"); 57 | static readonly arrow_down: ImageURISource = require("../../resources/images/arrow_down.png"); 58 | static readonly arrow_up: ImageURISource = require("../../resources/images/arrow_up.png"); 59 | static readonly avatar: ImageURISource = require("../../resources/images/avatar.png"); 60 | static readonly back: ImageURISource = require("../../resources/images/back.png"); 61 | static readonly bank: ImageURISource = require("../../resources/images/bank.png"); 62 | static readonly bell: ImageURISource = require("../../resources/images/bell.png"); 63 | } 64 | ``` 65 | 4. After this use it anywhere you need: 66 | ```typescript jsx 67 | 68 | ``` 69 | 70 | If you added or removed images, simply re-run the script to regenerate the file. 71 | 72 | ### Options 73 | 74 | | Option | Description | Required | Example | 75 | |--------|----------------------------------------------------------------------------|----------|----------------------------------------------------| 76 | | dir | Relative directory path with images | `True` | `img-res-gen --dir=resources/images` | 77 | | out | Output file path | `True` | `img-res-gen --out=src/common/ImageResources.g.ts` | 78 | | read | Read directory path, adds additional path to a file's output required path | `False` | `img-res-gen --read=build/src` | 79 | | ts | Should the output file be generated as a TypeScript file | `False` | `img-res-gen --ts` | 80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-native-image-resource-generator", 3 | "version": "1.1.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "react-native-image-resource-generator", 9 | "version": "1.1.0", 10 | "license": "MIT", 11 | "dependencies": { 12 | "command-line-args": "^6.0.1", 13 | "command-line-usage": "^7.0.3", 14 | "transliteration": "^2.3.5" 15 | }, 16 | "bin": { 17 | "img-res-gen": "bin/index.js" 18 | } 19 | }, 20 | "node_modules/ansi-regex": { 21 | "version": "5.0.1", 22 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", 23 | "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", 24 | "license": "MIT", 25 | "engines": { 26 | "node": ">=8" 27 | } 28 | }, 29 | "node_modules/ansi-styles": { 30 | "version": "4.3.0", 31 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", 32 | "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", 33 | "license": "MIT", 34 | "dependencies": { 35 | "color-convert": "^2.0.1" 36 | }, 37 | "engines": { 38 | "node": ">=8" 39 | }, 40 | "funding": { 41 | "url": "https://github.com/chalk/ansi-styles?sponsor=1" 42 | } 43 | }, 44 | "node_modules/array-back": { 45 | "version": "6.2.2", 46 | "resolved": "https://registry.npmjs.org/array-back/-/array-back-6.2.2.tgz", 47 | "integrity": "sha512-gUAZ7HPyb4SJczXAMUXMGAvI976JoK3qEx9v1FTmeYuJj0IBiaKttG1ydtGKdkfqWkIkouke7nG8ufGy77+Cvw==", 48 | "license": "MIT", 49 | "engines": { 50 | "node": ">=12.17" 51 | } 52 | }, 53 | "node_modules/chalk": { 54 | "version": "4.1.2", 55 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", 56 | "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", 57 | "license": "MIT", 58 | "dependencies": { 59 | "ansi-styles": "^4.1.0", 60 | "supports-color": "^7.1.0" 61 | }, 62 | "engines": { 63 | "node": ">=10" 64 | }, 65 | "funding": { 66 | "url": "https://github.com/chalk/chalk?sponsor=1" 67 | } 68 | }, 69 | "node_modules/chalk-template": { 70 | "version": "0.4.0", 71 | "resolved": "https://registry.npmjs.org/chalk-template/-/chalk-template-0.4.0.tgz", 72 | "integrity": "sha512-/ghrgmhfY8RaSdeo43hNXxpoHAtxdbskUHjPpfqUWGttFgycUhYPGx3YZBCnUCvOa7Doivn1IZec3DEGFoMgLg==", 73 | "license": "MIT", 74 | "dependencies": { 75 | "chalk": "^4.1.2" 76 | }, 77 | "engines": { 78 | "node": ">=12" 79 | }, 80 | "funding": { 81 | "url": "https://github.com/chalk/chalk-template?sponsor=1" 82 | } 83 | }, 84 | "node_modules/cliui": { 85 | "version": "8.0.1", 86 | "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", 87 | "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", 88 | "license": "ISC", 89 | "dependencies": { 90 | "string-width": "^4.2.0", 91 | "strip-ansi": "^6.0.1", 92 | "wrap-ansi": "^7.0.0" 93 | }, 94 | "engines": { 95 | "node": ">=12" 96 | } 97 | }, 98 | "node_modules/color-convert": { 99 | "version": "2.0.1", 100 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", 101 | "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", 102 | "license": "MIT", 103 | "dependencies": { 104 | "color-name": "~1.1.4" 105 | }, 106 | "engines": { 107 | "node": ">=7.0.0" 108 | } 109 | }, 110 | "node_modules/color-name": { 111 | "version": "1.1.4", 112 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 113 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", 114 | "license": "MIT" 115 | }, 116 | "node_modules/command-line-args": { 117 | "version": "6.0.1", 118 | "resolved": "https://registry.npmjs.org/command-line-args/-/command-line-args-6.0.1.tgz", 119 | "integrity": "sha512-Jr3eByUjqyK0qd8W0SGFW1nZwqCaNCtbXjRo2cRJC1OYxWl3MZ5t1US3jq+cO4sPavqgw4l9BMGX0CBe+trepg==", 120 | "license": "MIT", 121 | "dependencies": { 122 | "array-back": "^6.2.2", 123 | "find-replace": "^5.0.2", 124 | "lodash.camelcase": "^4.3.0", 125 | "typical": "^7.2.0" 126 | }, 127 | "engines": { 128 | "node": ">=12.20" 129 | }, 130 | "peerDependencies": { 131 | "@75lb/nature": "latest" 132 | }, 133 | "peerDependenciesMeta": { 134 | "@75lb/nature": { 135 | "optional": true 136 | } 137 | } 138 | }, 139 | "node_modules/command-line-usage": { 140 | "version": "7.0.3", 141 | "resolved": "https://registry.npmjs.org/command-line-usage/-/command-line-usage-7.0.3.tgz", 142 | "integrity": "sha512-PqMLy5+YGwhMh1wS04mVG44oqDsgyLRSKJBdOo1bnYhMKBW65gZF1dRp2OZRhiTjgUHljy99qkO7bsctLaw35Q==", 143 | "license": "MIT", 144 | "dependencies": { 145 | "array-back": "^6.2.2", 146 | "chalk-template": "^0.4.0", 147 | "table-layout": "^4.1.0", 148 | "typical": "^7.1.1" 149 | }, 150 | "engines": { 151 | "node": ">=12.20.0" 152 | } 153 | }, 154 | "node_modules/emoji-regex": { 155 | "version": "8.0.0", 156 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", 157 | "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", 158 | "license": "MIT" 159 | }, 160 | "node_modules/escalade": { 161 | "version": "3.2.0", 162 | "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", 163 | "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", 164 | "license": "MIT", 165 | "engines": { 166 | "node": ">=6" 167 | } 168 | }, 169 | "node_modules/find-replace": { 170 | "version": "5.0.2", 171 | "resolved": "https://registry.npmjs.org/find-replace/-/find-replace-5.0.2.tgz", 172 | "integrity": "sha512-Y45BAiE3mz2QsrN2fb5QEtO4qb44NcS7en/0y9PEVsg351HsLeVclP8QPMH79Le9sH3rs5RSwJu99W0WPZO43Q==", 173 | "license": "MIT", 174 | "engines": { 175 | "node": ">=14" 176 | }, 177 | "peerDependencies": { 178 | "@75lb/nature": "latest" 179 | }, 180 | "peerDependenciesMeta": { 181 | "@75lb/nature": { 182 | "optional": true 183 | } 184 | } 185 | }, 186 | "node_modules/get-caller-file": { 187 | "version": "2.0.5", 188 | "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", 189 | "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", 190 | "license": "ISC", 191 | "engines": { 192 | "node": "6.* || 8.* || >= 10.*" 193 | } 194 | }, 195 | "node_modules/has-flag": { 196 | "version": "4.0.0", 197 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", 198 | "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", 199 | "license": "MIT", 200 | "engines": { 201 | "node": ">=8" 202 | } 203 | }, 204 | "node_modules/is-fullwidth-code-point": { 205 | "version": "3.0.0", 206 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", 207 | "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", 208 | "license": "MIT", 209 | "engines": { 210 | "node": ">=8" 211 | } 212 | }, 213 | "node_modules/lodash.camelcase": { 214 | "version": "4.3.0", 215 | "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", 216 | "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", 217 | "license": "MIT" 218 | }, 219 | "node_modules/require-directory": { 220 | "version": "2.1.1", 221 | "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", 222 | "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", 223 | "license": "MIT", 224 | "engines": { 225 | "node": ">=0.10.0" 226 | } 227 | }, 228 | "node_modules/string-width": { 229 | "version": "4.2.3", 230 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", 231 | "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", 232 | "license": "MIT", 233 | "dependencies": { 234 | "emoji-regex": "^8.0.0", 235 | "is-fullwidth-code-point": "^3.0.0", 236 | "strip-ansi": "^6.0.1" 237 | }, 238 | "engines": { 239 | "node": ">=8" 240 | } 241 | }, 242 | "node_modules/strip-ansi": { 243 | "version": "6.0.1", 244 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", 245 | "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", 246 | "license": "MIT", 247 | "dependencies": { 248 | "ansi-regex": "^5.0.1" 249 | }, 250 | "engines": { 251 | "node": ">=8" 252 | } 253 | }, 254 | "node_modules/supports-color": { 255 | "version": "7.2.0", 256 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", 257 | "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", 258 | "license": "MIT", 259 | "dependencies": { 260 | "has-flag": "^4.0.0" 261 | }, 262 | "engines": { 263 | "node": ">=8" 264 | } 265 | }, 266 | "node_modules/table-layout": { 267 | "version": "4.1.1", 268 | "resolved": "https://registry.npmjs.org/table-layout/-/table-layout-4.1.1.tgz", 269 | "integrity": "sha512-iK5/YhZxq5GO5z8wb0bY1317uDF3Zjpha0QFFLA8/trAoiLbQD0HUbMesEaxyzUgDxi2QlcbM8IvqOlEjgoXBA==", 270 | "license": "MIT", 271 | "dependencies": { 272 | "array-back": "^6.2.2", 273 | "wordwrapjs": "^5.1.0" 274 | }, 275 | "engines": { 276 | "node": ">=12.17" 277 | } 278 | }, 279 | "node_modules/transliteration": { 280 | "version": "2.3.5", 281 | "resolved": "https://registry.npmjs.org/transliteration/-/transliteration-2.3.5.tgz", 282 | "integrity": "sha512-HAGI4Lq4Q9dZ3Utu2phaWgtm3vB6PkLUFqWAScg/UW+1eZ/Tg6Exo4oC0/3VUol/w4BlefLhUUSVBr/9/ZGQOw==", 283 | "license": "MIT", 284 | "dependencies": { 285 | "yargs": "^17.5.1" 286 | }, 287 | "bin": { 288 | "slugify": "dist/bin/slugify", 289 | "transliterate": "dist/bin/transliterate" 290 | }, 291 | "engines": { 292 | "node": ">=6.0.0" 293 | } 294 | }, 295 | "node_modules/typical": { 296 | "version": "7.3.0", 297 | "resolved": "https://registry.npmjs.org/typical/-/typical-7.3.0.tgz", 298 | "integrity": "sha512-ya4mg/30vm+DOWfBg4YK3j2WD6TWtRkCbasOJr40CseYENzCUby/7rIvXA99JGsQHeNxLbnXdyLLxKSv3tauFw==", 299 | "license": "MIT", 300 | "engines": { 301 | "node": ">=12.17" 302 | } 303 | }, 304 | "node_modules/wordwrapjs": { 305 | "version": "5.1.0", 306 | "resolved": "https://registry.npmjs.org/wordwrapjs/-/wordwrapjs-5.1.0.tgz", 307 | "integrity": "sha512-JNjcULU2e4KJwUNv6CHgI46UvDGitb6dGryHajXTDiLgg1/RiGoPSDw4kZfYnwGtEXf2ZMeIewDQgFGzkCB2Sg==", 308 | "license": "MIT", 309 | "engines": { 310 | "node": ">=12.17" 311 | } 312 | }, 313 | "node_modules/wrap-ansi": { 314 | "version": "7.0.0", 315 | "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", 316 | "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", 317 | "license": "MIT", 318 | "dependencies": { 319 | "ansi-styles": "^4.0.0", 320 | "string-width": "^4.1.0", 321 | "strip-ansi": "^6.0.0" 322 | }, 323 | "engines": { 324 | "node": ">=10" 325 | }, 326 | "funding": { 327 | "url": "https://github.com/chalk/wrap-ansi?sponsor=1" 328 | } 329 | }, 330 | "node_modules/y18n": { 331 | "version": "5.0.8", 332 | "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", 333 | "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", 334 | "license": "ISC", 335 | "engines": { 336 | "node": ">=10" 337 | } 338 | }, 339 | "node_modules/yargs": { 340 | "version": "17.7.2", 341 | "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", 342 | "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", 343 | "license": "MIT", 344 | "dependencies": { 345 | "cliui": "^8.0.1", 346 | "escalade": "^3.1.1", 347 | "get-caller-file": "^2.0.5", 348 | "require-directory": "^2.1.1", 349 | "string-width": "^4.2.3", 350 | "y18n": "^5.0.5", 351 | "yargs-parser": "^21.1.1" 352 | }, 353 | "engines": { 354 | "node": ">=12" 355 | } 356 | }, 357 | "node_modules/yargs-parser": { 358 | "version": "21.1.1", 359 | "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", 360 | "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", 361 | "license": "ISC", 362 | "engines": { 363 | "node": ">=12" 364 | } 365 | } 366 | } 367 | } 368 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-native-image-resource-generator", 3 | "version": "1.1.0", 4 | "description": "React Native image resource generator CLI tool", 5 | "main": "bin/index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "bin": { 10 | "img-res-gen": "bin/index.js" 11 | }, 12 | "files": [ 13 | "/bin" 14 | ], 15 | "repository": { 16 | "type": "git", 17 | "url": "git+https://github.com/svbutko/react-native-image-resource-generator.git" 18 | }, 19 | "keywords": [ 20 | "react-native", 21 | "image", 22 | "resources", 23 | "generator", 24 | "cli-tool" 25 | ], 26 | "author": "Sergei Butko (https://github.com/svbutko)", 27 | "license": "MIT", 28 | "bugs": { 29 | "url": "https://github.com/svbutko/react-native-image-resource-generator/issues" 30 | }, 31 | "homepage": "https://github.com/svbutko/react-native-image-resource-generator#readme", 32 | "contributors": [ 33 | { 34 | "name": "Sergei Butko", 35 | "url": "https://github.com/svbutko" 36 | }, 37 | { 38 | "name": "Igor Antsiferov", 39 | "url": "https://github.com/foerra" 40 | } 41 | ], 42 | "dependencies": { 43 | "command-line-args": "^6.0.1", 44 | "command-line-usage": "^7.0.3", 45 | "transliteration": "^2.3.5" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const transliteration = require("transliteration"); 4 | const fs = require("fs"); 5 | const path = require("path"); 6 | const commandLineArgs = require("command-line-args"); 7 | const commandLineUsage = require("command-line-usage"); 8 | 9 | const optionDefinitions = [ 10 | {name: "dir", alias: "d", type: String, description: "Relative directory path with images"}, 11 | {name: "out", alias: "o", type: String, description: "Output file path"}, 12 | {name: "read", alias: "r", type: String, description: "Read directory path (optional)"}, 13 | { 14 | name: "ts", 15 | alias: "t", 16 | type: Boolean, 17 | description: "Should the output file be generated as a TypeScript file (optional)", 18 | }, 19 | ]; 20 | 21 | const cmdOptions = commandLineArgs(optionDefinitions); 22 | const cmdUsage = commandLineUsage([{header: "Options", optionList: optionDefinitions}]); 23 | 24 | const entriesRegex = new RegExp("^((?!@).)*$"); 25 | 26 | run() 27 | .then(() => console.log("Done")) 28 | .catch((error) => console.error(`Error happened while generating resources:\n${error}`)); 29 | 30 | /** 31 | * @param {string} dir 32 | * @param {string} out 33 | * @param {string} fileName 34 | * @returns {FileEntry} 35 | */ 36 | function assembleFileEntry(dir, out, fileName) { 37 | return { 38 | relativeResourcePath: path.relative(path.dirname(out), path.join(dir, fileName)).replace(/\\/g, "/"), 39 | variableName: fileName 40 | .toLowerCase() 41 | .replace(/(.*)(.(png|jpg|jpeg|gif|bmp|svg))$/, "$1") 42 | .replace(/\W+/g, "_"), 43 | }; 44 | } 45 | 46 | async function run() { 47 | checkOptions(cmdOptions); 48 | console.log(`Started searching resources in ${cmdOptions.dir}`); 49 | 50 | const resources = []; 51 | 52 | let content = 53 | `/* eslint-disable */` + 54 | `\n/* tslint:disable */` + 55 | `${cmdOptions.ts ? '\nimport {ImageURISource} from "react-native";' : ""}` + 56 | `\n\n/**` + 57 | `\n * This file is auto-generated by react-native-image-resource-generator` + 58 | `\n * !!! DO NOT EDIT !!!` + 59 | `\n * For more information check the documentation:` + 60 | `\n * https://github.com/svbutko/react-native-image-resource-generator` + 61 | `\n*/`; 62 | 63 | await prepareFiles(cmdOptions.dir); 64 | await collectEntries(cmdOptions.dir, path.join("root", path.dirname(cmdOptions.out), cmdOptions.read || ""), true, resources); 65 | 66 | for (const resourceEntry of resources) { 67 | content += generateClassExport(resourceEntry.name, resourceEntry.entries); 68 | } 69 | 70 | fs.writeFileSync(cmdOptions.out, content); 71 | } 72 | 73 | /** 74 | * @param {string} dir 75 | * @param {string} out 76 | * @param {boolean} isRoot 77 | * @param {ResultEntries[]} result 78 | * @returns {Promise} 79 | */ 80 | async function collectEntries(dir, out, isRoot, result) { 81 | const files = await readDir(dir); 82 | 83 | /** @type {ResultEntries} */ 84 | const item = { 85 | name: isRoot ? "ImageResources" : toCamelCase(dir.split(path.sep).pop()) + "Resources", 86 | entries: [], 87 | }; 88 | 89 | for (const file of files) { 90 | if (file === '.DS_Store') { 91 | continue; 92 | } 93 | 94 | const joinedPath = path.join(dir, file); 95 | 96 | if (fs.lstatSync(joinedPath).isDirectory()) { 97 | await collectEntries(joinedPath, out, false, result); 98 | } else if (entriesRegex.exec(file)) { 99 | item.entries.push(assembleFileEntry(dir, out, file)); 100 | } 101 | } 102 | 103 | result.push(item); 104 | } 105 | 106 | /** 107 | * @param {string} className 108 | * @param {FileEntry[]} entries 109 | * @returns {string} 110 | */ 111 | function generateClassExport(className, entries) { 112 | return `\n\nexport class ${className} {\n${entries.map((entry) => getEntryDeclaration(entry)).join("\n")}\n}`; 113 | } 114 | 115 | /** 116 | * @param {FileEntry} entry 117 | * @returns {string} 118 | */ 119 | function getEntryDeclaration(entry) { 120 | if (cmdOptions.ts) { 121 | return ` static readonly ${entry.variableName}: ImageURISource = require("${entry.relativeResourcePath}");`; 122 | } 123 | 124 | return ` static ${entry.variableName} = require("${entry.relativeResourcePath}");`; 125 | } 126 | 127 | /** 128 | * @param {string} dir 129 | * @returns {Promise} 130 | */ 131 | function readDir(dir) { 132 | return new Promise((resolve, reject) => { 133 | fs.readdir(dir, (err, files) => { 134 | if (err) { 135 | reject(err); 136 | } 137 | resolve(files); 138 | }); 139 | }); 140 | } 141 | 142 | /** 143 | * @param {string} str 144 | * @returns {string} 145 | */ 146 | function toCamelCase(str) { 147 | return str.substring(0, 1).toUpperCase() + str.substring(1); 148 | } 149 | 150 | /** 151 | * @param {Options} options 152 | * @returns {void} 153 | */ 154 | function checkOptions(options) { 155 | if (options.dir == null || options.out == null) { 156 | throw new Error(`Missing non-optional options.\nList of options:\n ${cmdUsage}`); 157 | } 158 | } 159 | 160 | /** @type {OptionsTransliterate} */ 161 | const transliterationOptions = { 162 | trim: true, 163 | }; 164 | 165 | /** 166 | * @param {string} dir 167 | * @returns {void} 168 | */ 169 | async function prepareFiles(dir) { 170 | const files = await readDir(dir); 171 | 172 | for (const file of files) { 173 | const joinedPath = path.join(dir, file); 174 | 175 | if (fs.lstatSync(joinedPath).isDirectory()) { 176 | await prepareFiles(joinedPath); 177 | } else { 178 | const escapedFile = transliteration.transliterate(file, transliterationOptions) 179 | .replace(/[,]/g, ".") 180 | .replace(/[^A-Za-z0-9_@.]/g, "_"); 181 | 182 | if (escapedFile !== file) { 183 | fs.renameSync(joinedPath, path.join(dir, escapedFile)); 184 | } 185 | } 186 | } 187 | } 188 | 189 | /** 190 | * @typedef Options 191 | * @prop {string} dir 192 | * @prop {string} out 193 | * @prop {string} read 194 | * @prop {string} ts 195 | */ 196 | 197 | /** 198 | * @typedef FileEntry 199 | * @prop {string} relativeResourcePath 200 | * @prop {string} variableName 201 | */ 202 | 203 | /** 204 | * @typedef ResultEntries 205 | * @prop {string} name 206 | * @prop {FileEntry[]} entries 207 | */ 208 | 209 | /** 210 | * @typedef {import('transliteration/dist/node/src/types').OptionsTransliterate} OptionsTransliterate 211 | */ 212 | --------------------------------------------------------------------------------