├── .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 | [](https://www.npmjs.com/package/react-native-image-resource-generator)
4 | [](https://www.npmjs.com/package/react-native-image-resource-generator)
5 |
6 | [](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 |
--------------------------------------------------------------------------------