react-native-icon-switcher 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/IconSwitcher.podspec +20 -0
- package/LICENSE +20 -0
- package/README.md +252 -2
- package/android/build.gradle +51 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/java/com/iconswitcher/IconSwitcher.kt +380 -0
- package/android/src/main/java/com/iconswitcher/IconSwitcherExceptions.kt +24 -0
- package/android/src/main/java/com/iconswitcher/IconSwitcherModule.kt +67 -0
- package/android/src/main/java/com/iconswitcher/IconSwitcherPackage.kt +30 -0
- package/android/src/test/AndroidManifest.xml +39 -0
- package/android/src/test/java/com/iconswitcher/IconSwitcherTest.kt +392 -0
- package/app.plugin.js +1 -0
- package/ios/ISIconSwitcher.h +49 -0
- package/ios/ISIconSwitcher.m +205 -0
- package/ios/IconSwitcher.h +5 -0
- package/ios/IconSwitcher.mm +100 -0
- package/jest/index.d.ts +31 -0
- package/jest/index.js +98 -0
- package/lib/module/NativeIconSwitcher.js +5 -0
- package/lib/module/NativeIconSwitcher.js.map +1 -0
- package/lib/module/config.js +6 -0
- package/lib/module/config.js.map +1 -0
- package/lib/module/errors.js +28 -0
- package/lib/module/errors.js.map +1 -0
- package/lib/module/index.js +69 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/module/register.js +2 -0
- package/lib/module/register.js.map +1 -0
- package/lib/module/remote/index.js +119 -0
- package/lib/module/remote/index.js.map +1 -0
- package/lib/module/types.js +2 -0
- package/lib/module/types.js.map +1 -0
- package/lib/module/typetests/icon-name.typetest.js +8 -0
- package/lib/module/typetests/icon-name.typetest.js.map +1 -0
- package/lib/module/typetests/types-in-sync.typetest.js +7 -0
- package/lib/module/typetests/types-in-sync.typetest.js.map +1 -0
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/src/NativeIconSwitcher.d.ts +18 -0
- package/lib/typescript/src/NativeIconSwitcher.d.ts.map +1 -0
- package/lib/typescript/src/config.d.ts +4 -0
- package/lib/typescript/src/config.d.ts.map +1 -0
- package/lib/typescript/src/errors.d.ts +7 -0
- package/lib/typescript/src/errors.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +20 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/lib/typescript/src/register.d.ts +7 -0
- package/lib/typescript/src/register.d.ts.map +1 -0
- package/lib/typescript/src/remote/index.d.ts +17 -0
- package/lib/typescript/src/remote/index.d.ts.map +1 -0
- package/lib/typescript/src/types.d.ts +59 -0
- package/lib/typescript/src/types.d.ts.map +1 -0
- package/package.json +251 -4
- package/plugin/build/cli.d.ts +3 -0
- package/plugin/build/cli.js +89 -0
- package/plugin/build/core/assets.d.ts +80 -0
- package/plugin/build/core/assets.js +463 -0
- package/plugin/build/core/errors.d.ts +4 -0
- package/plugin/build/core/errors.js +11 -0
- package/plugin/build/core/validate.d.ts +6 -0
- package/plugin/build/core/validate.js +229 -0
- package/plugin/build/index.d.ts +6 -0
- package/plugin/build/index.js +134 -0
- package/plugin/build/load-config.d.ts +3 -0
- package/plugin/build/load-config.js +50 -0
- package/plugin/build/paths.d.ts +15 -0
- package/plugin/build/paths.js +153 -0
- package/plugin/build/sync.d.ts +12 -0
- package/plugin/build/sync.js +162 -0
- package/plugin/build/transforms/android.d.ts +5 -0
- package/plugin/build/transforms/android.js +212 -0
- package/plugin/build/transforms/errors.d.ts +4 -0
- package/plugin/build/transforms/errors.js +11 -0
- package/plugin/build/transforms/index.d.ts +4 -0
- package/plugin/build/transforms/index.js +12 -0
- package/plugin/build/transforms/ios.d.ts +4 -0
- package/plugin/build/transforms/ios.js +111 -0
- package/plugin/build/transforms/sampleConfigs.d.ts +3 -0
- package/plugin/build/transforms/sampleConfigs.js +33 -0
- package/plugin/build/transforms/types.d.ts +2 -0
- package/plugin/build/transforms/types.js +14 -0
- package/plugin/build/types.d.ts +61 -0
- package/plugin/build/types.js +5 -0
- package/src/NativeIconSwitcher.ts +17 -0
- package/src/config.ts +15 -0
- package/src/errors.ts +38 -0
- package/src/index.tsx +78 -0
- package/src/register.ts +4 -0
- package/src/remote/index.ts +142 -0
- package/src/types.ts +59 -0
- package/src/typetests/icon-name.typetest.ts +12 -0
- package/src/typetests/types-in-sync.typetest.ts +16 -0
|
@@ -0,0 +1,463 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.planAssets = planAssets;
|
|
7
|
+
exports.writeAssets = writeAssets;
|
|
8
|
+
exports._readCornerRgb = _readCornerRgb;
|
|
9
|
+
const node_crypto_1 = __importDefault(require("node:crypto"));
|
|
10
|
+
const promises_1 = __importDefault(require("node:fs/promises"));
|
|
11
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
12
|
+
const sharp_1 = __importDefault(require("sharp"));
|
|
13
|
+
const LEGACY_DENSITIES = [
|
|
14
|
+
{ folder: 'mipmap-mdpi', size: 48 },
|
|
15
|
+
{ folder: 'mipmap-hdpi', size: 72 },
|
|
16
|
+
{ folder: 'mipmap-xhdpi', size: 96 },
|
|
17
|
+
{ folder: 'mipmap-xxhdpi', size: 144 },
|
|
18
|
+
{ folder: 'mipmap-xxxhdpi', size: 192 },
|
|
19
|
+
];
|
|
20
|
+
const ADAPTIVE_DENSITIES = [
|
|
21
|
+
{ folder: 'mipmap-mdpi', size: 108 },
|
|
22
|
+
{ folder: 'mipmap-hdpi', size: 162 },
|
|
23
|
+
{ folder: 'mipmap-xhdpi', size: 216 },
|
|
24
|
+
{ folder: 'mipmap-xxhdpi', size: 324 },
|
|
25
|
+
{ folder: 'mipmap-xxxhdpi', size: 432 },
|
|
26
|
+
];
|
|
27
|
+
const PNG_OPTIONS = { compressionLevel: 9, adaptiveFiltering: false };
|
|
28
|
+
function _iosSetFolder(icon) {
|
|
29
|
+
return icon.isDefault
|
|
30
|
+
? 'AppIcon.appiconset'
|
|
31
|
+
: `AppIcon-${icon.name}.appiconset`;
|
|
32
|
+
}
|
|
33
|
+
function _androidBaseName(icon) {
|
|
34
|
+
return icon.isDefault ? 'ic_launcher' : `ic_launcher_${icon.name}`;
|
|
35
|
+
}
|
|
36
|
+
function _colorResourceName(icon) {
|
|
37
|
+
return icon.isDefault
|
|
38
|
+
? 'icon_switcher_default_background'
|
|
39
|
+
: `icon_switcher_${icon.name}_background`;
|
|
40
|
+
}
|
|
41
|
+
function _iosVariants(icon) {
|
|
42
|
+
if (!icon.ios) {
|
|
43
|
+
return [];
|
|
44
|
+
}
|
|
45
|
+
const out = [
|
|
46
|
+
{ variant: 'light', source: icon.ios.light },
|
|
47
|
+
];
|
|
48
|
+
if (icon.ios.dark) {
|
|
49
|
+
out.push({ variant: 'dark', source: icon.ios.dark });
|
|
50
|
+
}
|
|
51
|
+
if (icon.ios.tinted) {
|
|
52
|
+
out.push({ variant: 'tinted', source: icon.ios.tinted });
|
|
53
|
+
}
|
|
54
|
+
return out;
|
|
55
|
+
}
|
|
56
|
+
function _legacySource(icon) {
|
|
57
|
+
const android = icon.android;
|
|
58
|
+
if (!android) {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
if ('legacy' in android) {
|
|
62
|
+
return android.legacy;
|
|
63
|
+
}
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
function _hasAdaptive(icon) {
|
|
67
|
+
const android = icon.android;
|
|
68
|
+
return android !== null && !('legacy' in android);
|
|
69
|
+
}
|
|
70
|
+
function planAssets(cfg, projectRoot) {
|
|
71
|
+
const assets = [];
|
|
72
|
+
const colorEntries = [];
|
|
73
|
+
for (const icon of cfg.icons) {
|
|
74
|
+
const setFolder = _iosSetFolder(icon);
|
|
75
|
+
const iosVariants = _iosVariants(icon);
|
|
76
|
+
if (iosVariants.length > 0) {
|
|
77
|
+
for (const { variant, source } of iosVariants) {
|
|
78
|
+
const filename = variant === 'light'
|
|
79
|
+
? 'AppIcon-1024.png'
|
|
80
|
+
: `AppIcon-1024-${variant}.png`;
|
|
81
|
+
assets.push({
|
|
82
|
+
relativePath: node_path_1.default.join(setFolder, filename),
|
|
83
|
+
job: {
|
|
84
|
+
kind: 'ios-png',
|
|
85
|
+
iconName: icon.name,
|
|
86
|
+
isDefault: icon.isDefault,
|
|
87
|
+
variant,
|
|
88
|
+
sourcePath: source,
|
|
89
|
+
flattenOn: cfg.ios.flattenOn,
|
|
90
|
+
width: 1024,
|
|
91
|
+
height: 1024,
|
|
92
|
+
},
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
assets.push({
|
|
96
|
+
relativePath: node_path_1.default.join(setFolder, 'Contents.json'),
|
|
97
|
+
job: {
|
|
98
|
+
kind: 'ios-contents',
|
|
99
|
+
iconName: icon.name,
|
|
100
|
+
isDefault: icon.isDefault,
|
|
101
|
+
variants: iosVariants.map((v) => v.variant),
|
|
102
|
+
},
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
const legacySource = _legacySource(icon);
|
|
106
|
+
if (legacySource) {
|
|
107
|
+
const base = _androidBaseName(icon);
|
|
108
|
+
for (const { folder, size } of LEGACY_DENSITIES) {
|
|
109
|
+
for (const round of [false, true]) {
|
|
110
|
+
assets.push({
|
|
111
|
+
relativePath: node_path_1.default.join(folder, `${base}${round ? '_round' : ''}.png`),
|
|
112
|
+
job: {
|
|
113
|
+
kind: 'android-legacy',
|
|
114
|
+
iconName: icon.name,
|
|
115
|
+
isDefault: icon.isDefault,
|
|
116
|
+
round,
|
|
117
|
+
sourcePath: legacySource,
|
|
118
|
+
density: folder,
|
|
119
|
+
size,
|
|
120
|
+
},
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
if (_hasAdaptive(icon)) {
|
|
126
|
+
const android = icon.android;
|
|
127
|
+
const base = _androidBaseName(icon);
|
|
128
|
+
const backgroundIsColor = typeof android.background === 'string' &&
|
|
129
|
+
android.background.startsWith('#');
|
|
130
|
+
const backgroundImagePath = typeof android.background === 'string' &&
|
|
131
|
+
!android.background.startsWith('#')
|
|
132
|
+
? android.background
|
|
133
|
+
: undefined;
|
|
134
|
+
for (const { folder, size } of LEGACY_DENSITIES) {
|
|
135
|
+
for (const round of [false, true]) {
|
|
136
|
+
assets.push({
|
|
137
|
+
relativePath: node_path_1.default.join(folder, `${base}${round ? '_round' : ''}.png`),
|
|
138
|
+
job: {
|
|
139
|
+
kind: 'android-legacy',
|
|
140
|
+
iconName: icon.name,
|
|
141
|
+
isDefault: icon.isDefault,
|
|
142
|
+
round,
|
|
143
|
+
sourcePath: android.foreground,
|
|
144
|
+
density: folder,
|
|
145
|
+
size,
|
|
146
|
+
backgroundColor: backgroundIsColor
|
|
147
|
+
? android.background
|
|
148
|
+
: undefined,
|
|
149
|
+
backgroundImagePath,
|
|
150
|
+
},
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
for (const { folder, size } of ADAPTIVE_DENSITIES) {
|
|
155
|
+
assets.push({
|
|
156
|
+
relativePath: node_path_1.default.join(folder, `${base}_foreground.png`),
|
|
157
|
+
job: {
|
|
158
|
+
kind: 'android-adaptive-foreground',
|
|
159
|
+
iconName: icon.name,
|
|
160
|
+
isDefault: icon.isDefault,
|
|
161
|
+
sourcePath: android.foreground,
|
|
162
|
+
density: folder,
|
|
163
|
+
size,
|
|
164
|
+
},
|
|
165
|
+
});
|
|
166
|
+
if (typeof android.background === 'string' &&
|
|
167
|
+
!android.background.startsWith('#')) {
|
|
168
|
+
assets.push({
|
|
169
|
+
relativePath: node_path_1.default.join(folder, `${base}_background.png`),
|
|
170
|
+
job: {
|
|
171
|
+
kind: 'android-adaptive-background-image',
|
|
172
|
+
iconName: icon.name,
|
|
173
|
+
isDefault: icon.isDefault,
|
|
174
|
+
sourcePath: android.background,
|
|
175
|
+
density: folder,
|
|
176
|
+
size,
|
|
177
|
+
},
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
if (android.monochrome) {
|
|
181
|
+
assets.push({
|
|
182
|
+
relativePath: node_path_1.default.join(folder, `${base}_monochrome.png`),
|
|
183
|
+
job: {
|
|
184
|
+
kind: 'android-adaptive-foreground',
|
|
185
|
+
iconName: icon.name,
|
|
186
|
+
isDefault: icon.isDefault,
|
|
187
|
+
sourcePath: android.monochrome,
|
|
188
|
+
density: folder,
|
|
189
|
+
size,
|
|
190
|
+
},
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
if (backgroundIsColor) {
|
|
195
|
+
colorEntries.push({
|
|
196
|
+
resourceName: _colorResourceName(icon),
|
|
197
|
+
color: android.background,
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
for (const round of [false, true]) {
|
|
201
|
+
assets.push({
|
|
202
|
+
relativePath: node_path_1.default.join('mipmap-anydpi-v26', `${base}${round ? '_round' : ''}.xml`),
|
|
203
|
+
job: {
|
|
204
|
+
kind: 'android-adaptive-xml',
|
|
205
|
+
iconName: icon.name,
|
|
206
|
+
isDefault: icon.isDefault,
|
|
207
|
+
round,
|
|
208
|
+
backgroundIsColor,
|
|
209
|
+
hasMonochrome: Boolean(android.monochrome),
|
|
210
|
+
},
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
if (colorEntries.length > 0) {
|
|
216
|
+
const unique = new Map();
|
|
217
|
+
for (const e of colorEntries) {
|
|
218
|
+
unique.set(e.resourceName, e.color);
|
|
219
|
+
}
|
|
220
|
+
assets.push({
|
|
221
|
+
relativePath: node_path_1.default.join('values', 'icon_switcher_colors.xml'),
|
|
222
|
+
job: {
|
|
223
|
+
kind: 'android-colors-xml',
|
|
224
|
+
entries: [...unique.entries()].map(([resourceName, color]) => ({
|
|
225
|
+
resourceName,
|
|
226
|
+
color,
|
|
227
|
+
})),
|
|
228
|
+
},
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
return {
|
|
232
|
+
projectRoot,
|
|
233
|
+
flattenOn: cfg.ios.flattenOn,
|
|
234
|
+
assets,
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
function _hexToRgb(hex) {
|
|
238
|
+
let normalized = hex.slice(1);
|
|
239
|
+
if (normalized.length === 3) {
|
|
240
|
+
normalized = normalized
|
|
241
|
+
.split('')
|
|
242
|
+
.map((c) => c + c)
|
|
243
|
+
.join('');
|
|
244
|
+
}
|
|
245
|
+
const num = Number.parseInt(normalized, 16);
|
|
246
|
+
return {
|
|
247
|
+
r: (num >> 16) & 0xff,
|
|
248
|
+
g: (num >> 8) & 0xff,
|
|
249
|
+
b: num & 0xff,
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
async function _flattenPng(sourceAbs, flattenOn, width, height) {
|
|
253
|
+
const { r, g, b } = _hexToRgb(flattenOn);
|
|
254
|
+
const background = await (0, sharp_1.default)({
|
|
255
|
+
create: {
|
|
256
|
+
width,
|
|
257
|
+
height,
|
|
258
|
+
channels: 3,
|
|
259
|
+
background: { r, g, b },
|
|
260
|
+
},
|
|
261
|
+
})
|
|
262
|
+
.png(PNG_OPTIONS)
|
|
263
|
+
.toBuffer();
|
|
264
|
+
const resized = await (0, sharp_1.default)(sourceAbs)
|
|
265
|
+
.resize(width, height, {
|
|
266
|
+
fit: 'contain',
|
|
267
|
+
background: { r, g, b, alpha: 0 },
|
|
268
|
+
})
|
|
269
|
+
.png()
|
|
270
|
+
.toBuffer();
|
|
271
|
+
return (0, sharp_1.default)(background)
|
|
272
|
+
.composite([{ input: resized, blend: 'over' }])
|
|
273
|
+
.removeAlpha()
|
|
274
|
+
.png(PNG_OPTIONS)
|
|
275
|
+
.toBuffer();
|
|
276
|
+
}
|
|
277
|
+
async function _applyRoundMask(image, size) {
|
|
278
|
+
const mask = Buffer.from(`<svg width="${size}" height="${size}"><circle cx="${size / 2}" cy="${size / 2}" r="${size / 2}" fill="white"/></svg>`);
|
|
279
|
+
return (0, sharp_1.default)(image)
|
|
280
|
+
.composite([{ input: mask, blend: 'dest-in' }])
|
|
281
|
+
.png(PNG_OPTIONS)
|
|
282
|
+
.toBuffer();
|
|
283
|
+
}
|
|
284
|
+
async function _legacyPng(sourceAbs, size, round) {
|
|
285
|
+
const resized = await (0, sharp_1.default)(sourceAbs)
|
|
286
|
+
.resize(size, size, { fit: 'cover' })
|
|
287
|
+
.png()
|
|
288
|
+
.toBuffer();
|
|
289
|
+
if (!round) {
|
|
290
|
+
return (0, sharp_1.default)(resized).png(PNG_OPTIONS).toBuffer();
|
|
291
|
+
}
|
|
292
|
+
return _applyRoundMask(resized, size);
|
|
293
|
+
}
|
|
294
|
+
async function _legacyCompositePng(foregroundAbs, size, round, background) {
|
|
295
|
+
let base;
|
|
296
|
+
if ('color' in background) {
|
|
297
|
+
const { r, g, b } = _hexToRgb(background.color);
|
|
298
|
+
base = await (0, sharp_1.default)({
|
|
299
|
+
create: {
|
|
300
|
+
width: size,
|
|
301
|
+
height: size,
|
|
302
|
+
channels: 4,
|
|
303
|
+
background: { r, g, b, alpha: 255 },
|
|
304
|
+
},
|
|
305
|
+
})
|
|
306
|
+
.png()
|
|
307
|
+
.toBuffer();
|
|
308
|
+
}
|
|
309
|
+
else {
|
|
310
|
+
base = await (0, sharp_1.default)(background.imageAbs)
|
|
311
|
+
.resize(size, size, { fit: 'cover' })
|
|
312
|
+
.png()
|
|
313
|
+
.toBuffer();
|
|
314
|
+
}
|
|
315
|
+
const foreground = await (0, sharp_1.default)(foregroundAbs)
|
|
316
|
+
.resize(size, size, {
|
|
317
|
+
fit: 'contain',
|
|
318
|
+
background: { r: 0, g: 0, b: 0, alpha: 0 },
|
|
319
|
+
})
|
|
320
|
+
.png()
|
|
321
|
+
.toBuffer();
|
|
322
|
+
const flat = await (0, sharp_1.default)(base)
|
|
323
|
+
.composite([{ input: foreground, blend: 'over' }])
|
|
324
|
+
.png(PNG_OPTIONS)
|
|
325
|
+
.toBuffer();
|
|
326
|
+
if (!round) {
|
|
327
|
+
return flat;
|
|
328
|
+
}
|
|
329
|
+
return _applyRoundMask(flat, size);
|
|
330
|
+
}
|
|
331
|
+
async function _resizePng(sourceAbs, size) {
|
|
332
|
+
return (0, sharp_1.default)(sourceAbs)
|
|
333
|
+
.resize(size, size, {
|
|
334
|
+
fit: 'contain',
|
|
335
|
+
background: { r: 0, g: 0, b: 0, alpha: 0 },
|
|
336
|
+
})
|
|
337
|
+
.png(PNG_OPTIONS)
|
|
338
|
+
.toBuffer();
|
|
339
|
+
}
|
|
340
|
+
function _iosContentsJson(variants) {
|
|
341
|
+
const images = variants.map((variant) => {
|
|
342
|
+
const base = {
|
|
343
|
+
idiom: 'universal',
|
|
344
|
+
platform: 'ios',
|
|
345
|
+
size: '1024x1024',
|
|
346
|
+
};
|
|
347
|
+
const filename = variant === 'light' ? 'AppIcon-1024.png' : `AppIcon-1024-${variant}.png`;
|
|
348
|
+
if (variant === 'light') {
|
|
349
|
+
return { ...base, filename };
|
|
350
|
+
}
|
|
351
|
+
const appearanceValue = variant === 'dark' ? 'dark' : 'tinted';
|
|
352
|
+
return {
|
|
353
|
+
...base,
|
|
354
|
+
filename,
|
|
355
|
+
appearances: [{ appearance: 'luminosity', value: appearanceValue }],
|
|
356
|
+
};
|
|
357
|
+
});
|
|
358
|
+
return `${JSON.stringify({
|
|
359
|
+
images,
|
|
360
|
+
info: { version: 1, author: 'xcode' },
|
|
361
|
+
}, null, 2)}\n`;
|
|
362
|
+
}
|
|
363
|
+
function _adaptiveXml(base, backgroundIsColor, colorResource, hasMonochrome) {
|
|
364
|
+
const backgroundRef = backgroundIsColor
|
|
365
|
+
? `@color/${colorResource}`
|
|
366
|
+
: `@mipmap/${base}_background`;
|
|
367
|
+
const lines = [
|
|
368
|
+
'<?xml version="1.0" encoding="utf-8"?>',
|
|
369
|
+
'<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">',
|
|
370
|
+
` <background android:drawable="${backgroundRef}" />`,
|
|
371
|
+
` <foreground android:drawable="@mipmap/${base}_foreground" />`,
|
|
372
|
+
];
|
|
373
|
+
if (hasMonochrome) {
|
|
374
|
+
lines.push(` <monochrome android:drawable="@mipmap/${base}_monochrome" />`);
|
|
375
|
+
}
|
|
376
|
+
lines.push('</adaptive-icon>', '');
|
|
377
|
+
return lines.join('\n');
|
|
378
|
+
}
|
|
379
|
+
function _colorsXml(entries) {
|
|
380
|
+
const lines = ['<?xml version="1.0" encoding="utf-8"?>', '<resources>'];
|
|
381
|
+
for (const { resourceName, color } of entries) {
|
|
382
|
+
lines.push(` <color name="${resourceName}">${color}</color>`);
|
|
383
|
+
}
|
|
384
|
+
lines.push('</resources>', '');
|
|
385
|
+
return lines.join('\n');
|
|
386
|
+
}
|
|
387
|
+
async function _writeBuffer(abs, data) {
|
|
388
|
+
await promises_1.default.mkdir(node_path_1.default.dirname(abs), { recursive: true });
|
|
389
|
+
await promises_1.default.writeFile(abs, data);
|
|
390
|
+
return node_crypto_1.default.createHash('sha256').update(data).digest('hex');
|
|
391
|
+
}
|
|
392
|
+
async function _writeText(abs, text) {
|
|
393
|
+
const data = Buffer.from(text, 'utf8');
|
|
394
|
+
return _writeBuffer(abs, data);
|
|
395
|
+
}
|
|
396
|
+
async function _renderJob(plan, job) {
|
|
397
|
+
const root = plan.projectRoot;
|
|
398
|
+
switch (job.kind) {
|
|
399
|
+
case 'ios-png':
|
|
400
|
+
return _flattenPng(node_path_1.default.join(root, job.sourcePath), job.flattenOn, job.width, job.height);
|
|
401
|
+
case 'ios-contents':
|
|
402
|
+
return _iosContentsJson(job.variants);
|
|
403
|
+
case 'android-legacy': {
|
|
404
|
+
const foregroundAbs = node_path_1.default.join(root, job.sourcePath);
|
|
405
|
+
if (job.backgroundColor !== undefined) {
|
|
406
|
+
return _legacyCompositePng(foregroundAbs, job.size, job.round, {
|
|
407
|
+
color: job.backgroundColor,
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
if (job.backgroundImagePath !== undefined) {
|
|
411
|
+
return _legacyCompositePng(foregroundAbs, job.size, job.round, {
|
|
412
|
+
imageAbs: node_path_1.default.join(root, job.backgroundImagePath),
|
|
413
|
+
});
|
|
414
|
+
}
|
|
415
|
+
return _legacyPng(foregroundAbs, job.size, job.round);
|
|
416
|
+
}
|
|
417
|
+
case 'android-adaptive-foreground':
|
|
418
|
+
case 'android-adaptive-background-image':
|
|
419
|
+
return _resizePng(node_path_1.default.join(root, job.sourcePath), job.size);
|
|
420
|
+
case 'android-adaptive-xml': {
|
|
421
|
+
const base = job.isDefault
|
|
422
|
+
? 'ic_launcher'
|
|
423
|
+
: `ic_launcher_${job.iconName}`;
|
|
424
|
+
const colorResource = job.isDefault
|
|
425
|
+
? 'icon_switcher_default_background'
|
|
426
|
+
: `icon_switcher_${job.iconName}_background`;
|
|
427
|
+
return _adaptiveXml(base, job.backgroundIsColor, colorResource, job.hasMonochrome);
|
|
428
|
+
}
|
|
429
|
+
case 'android-colors-xml':
|
|
430
|
+
return _colorsXml(job.entries);
|
|
431
|
+
default: {
|
|
432
|
+
const _exhaustive = job;
|
|
433
|
+
return _exhaustive;
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
async function writeAssets(plan, opts) {
|
|
438
|
+
const written = [];
|
|
439
|
+
for (const asset of plan.assets) {
|
|
440
|
+
const isIos = asset.relativePath.includes('.appiconset');
|
|
441
|
+
const baseDir = isIos ? opts.iosAssetCatalog : opts.androidResDir;
|
|
442
|
+
const absolutePath = node_path_1.default.join(baseDir, asset.relativePath);
|
|
443
|
+
const payload = await _renderJob(plan, asset.job);
|
|
444
|
+
const sha256 = typeof payload === 'string'
|
|
445
|
+
? await _writeText(absolutePath, payload)
|
|
446
|
+
: await _writeBuffer(absolutePath, payload);
|
|
447
|
+
written.push({
|
|
448
|
+
relativePath: asset.relativePath,
|
|
449
|
+
absolutePath,
|
|
450
|
+
sha256,
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
return written;
|
|
454
|
+
}
|
|
455
|
+
/** Test helper: corner pixel RGB after iOS flatten (alpha removed). */
|
|
456
|
+
async function _readCornerRgb(pngPath) {
|
|
457
|
+
var _a, _b, _c;
|
|
458
|
+
const { data } = await (0, sharp_1.default)(pngPath)
|
|
459
|
+
.extract({ left: 0, top: 0, width: 1, height: 1 })
|
|
460
|
+
.raw()
|
|
461
|
+
.toBuffer({ resolveWithObject: true });
|
|
462
|
+
return { r: (_a = data[0]) !== null && _a !== void 0 ? _a : 0, g: (_b = data[1]) !== null && _b !== void 0 ? _b : 0, b: (_c = data[2]) !== null && _c !== void 0 ? _c : 0 };
|
|
463
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ConfigError = void 0;
|
|
4
|
+
class ConfigError extends Error {
|
|
5
|
+
constructor(message) {
|
|
6
|
+
super(message);
|
|
7
|
+
this.name = 'ConfigError';
|
|
8
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
exports.ConfigError = ConfigError;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { IconSwitcherConfig, IconSource, ResolvedConfig } from '../types';
|
|
2
|
+
export interface ResolveConfigContext {
|
|
3
|
+
projectRoot: string;
|
|
4
|
+
expoDefault?: IconSource;
|
|
5
|
+
}
|
|
6
|
+
export declare function resolveConfig(cfg: IconSwitcherConfig, ctx: ResolveConfigContext): ResolvedConfig;
|