react-native-bootsplash 6.3.10 → 7.0.0-beta.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/README.md +16 -41
- package/android/build.gradle +10 -17
- package/android/src/main/AndroidManifest.xml +1 -1
- package/android/src/main/java/com/zoontek/rnbootsplash/RNBootSplashModuleImpl.kt +2 -2
- package/android/src/main/java/com/zoontek/rnbootsplash/RNBootSplashPackage.kt +0 -1
- package/android/src/main/java/com/zoontek/rnbootsplash/RNBootSplashQueue.kt +1 -1
- package/android/src/main/res/drawable/compat_splash_screen.xml +11 -6
- package/android/src/main/res/drawable/compat_splash_screen_oneui_4.xml +13 -6
- package/android/src/main/res/values/colors.xml +1 -3
- package/android/src/main/res/values/public.xml +0 -2
- package/android/src/main/res/values/styles.xml +13 -19
- package/android/src/main/res/values-night/styles.xml +1 -1
- package/android/src/main/res/values-v27/colors.xml +4 -0
- package/android/src/main/res/values-v27/styles.xml +1 -6
- package/android/src/main/res/values-v29/styles.xml +1 -11
- package/android/src/main/res/values-v30/styles.xml +1 -9
- package/android/src/main/res/values-v31/styles.xml +1 -1
- package/android/src/newarch/com/zoontek/rnbootsplash/RNBootSplashModule.kt +2 -2
- package/android/src/oldarch/com/zoontek/rnbootsplash/RNBootSplashModule.kt +3 -3
- package/app.plugin.js +1 -1
- package/cli.js +2 -11
- package/dist/commonjs/addon/index.js +49 -49
- package/dist/commonjs/addon/index.js.map +1 -1
- package/dist/commonjs/generate.js +811 -541
- package/dist/commonjs/generate.js.map +1 -1
- package/dist/commonjs/index.js +1 -1
- package/dist/commonjs/index.js.map +1 -1
- package/dist/commonjs/specs/NativeRNBootSplash.js.map +1 -0
- package/dist/commonjs/{NativeRNBootSplash.web.js → specs/NativeRNBootSplash.web.js} +1 -1
- package/dist/commonjs/specs/NativeRNBootSplash.web.js.map +1 -0
- package/dist/module/addon/index.js +49 -49
- package/dist/module/addon/index.js.map +1 -1
- package/dist/module/generate.js +807 -539
- package/dist/module/generate.js.map +1 -1
- package/dist/module/index.js +1 -1
- package/dist/module/index.js.map +1 -1
- package/dist/module/specs/NativeRNBootSplash.js.map +1 -0
- package/dist/module/{NativeRNBootSplash.web.js → specs/NativeRNBootSplash.web.js} +1 -1
- package/dist/module/specs/NativeRNBootSplash.web.js.map +1 -0
- package/dist/typescript/addon/index.d.ts +8 -2
- package/dist/typescript/addon/index.d.ts.map +1 -1
- package/dist/typescript/generate.d.ts +90 -35
- package/dist/typescript/generate.d.ts.map +1 -1
- package/dist/typescript/index.d.ts +2 -2
- package/dist/typescript/index.d.ts.map +1 -1
- package/dist/typescript/{NativeRNBootSplash.d.ts → specs/NativeRNBootSplash.d.ts} +1 -1
- package/dist/typescript/specs/NativeRNBootSplash.d.ts.map +1 -0
- package/dist/typescript/{NativeRNBootSplash.web.d.ts → specs/NativeRNBootSplash.web.d.ts} +1 -1
- package/dist/typescript/specs/NativeRNBootSplash.web.d.ts.map +1 -0
- package/ios/RNBootSplash.mm +4 -10
- package/package.json +24 -29
- package/src/generate.ts +1048 -782
- package/src/index.ts +7 -7
- package/src/{NativeRNBootSplash.ts → specs/NativeRNBootSplash.ts} +1 -1
- package/src/{NativeRNBootSplash.web.ts → specs/NativeRNBootSplash.web.ts} +1 -1
- package/android/src/main/AndroidManifestNew.xml +0 -3
- package/android/src/main/res/drawable-v23/compat_splash_screen.xml +0 -19
- package/android/src/main/res/drawable-v23/compat_splash_screen_oneui_4.xml +0 -21
- package/android/src/main/res/values-v23/styles.xml +0 -13
- package/dist/commonjs/NativeRNBootSplash.js.map +0 -1
- package/dist/commonjs/NativeRNBootSplash.web.js.map +0 -1
- package/dist/commonjs/expo.js +0 -348
- package/dist/commonjs/expo.js.map +0 -1
- package/dist/module/NativeRNBootSplash.js.map +0 -1
- package/dist/module/NativeRNBootSplash.web.js.map +0 -1
- package/dist/module/expo.js +0 -341
- package/dist/module/expo.js.map +0 -1
- package/dist/typescript/NativeRNBootSplash.d.ts.map +0 -1
- package/dist/typescript/NativeRNBootSplash.web.d.ts.map +0 -1
- package/dist/typescript/expo.d.ts +0 -11
- package/dist/typescript/expo.d.ts.map +0 -1
- package/react-native.config.js +0 -101
- package/src/expo.ts +0 -439
- /package/dist/commonjs/{NativeRNBootSplash.js → specs/NativeRNBootSplash.js} +0 -0
- /package/dist/module/{NativeRNBootSplash.js → specs/NativeRNBootSplash.js} +0 -0
package/dist/module/generate.js
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
3
|
import * as Expo from "@expo/config-plugins";
|
|
4
|
-
import
|
|
4
|
+
import { assignColorValue } from "@expo/config-plugins/build/android/Colors";
|
|
5
|
+
import { addImports } from "@expo/config-plugins/build/android/codeMod";
|
|
6
|
+
import { mergeContents } from "@expo/config-plugins/build/utils/generateCode";
|
|
7
|
+
import ExpoPlist from "@expo/plist";
|
|
5
8
|
import { projectConfig as getAndroidProjectConfig } from "@react-native-community/cli-config-android";
|
|
6
9
|
import { getProjectConfig as getAppleProjectConfig } from "@react-native-community/cli-config-apple";
|
|
7
10
|
import { findProjectRoot } from "@react-native-community/cli-tools";
|
|
@@ -20,13 +23,10 @@ import sharp from "sharp";
|
|
|
20
23
|
import { dedent } from "ts-dedent";
|
|
21
24
|
import util from "util";
|
|
22
25
|
import formatXml from "xml-formatter";
|
|
23
|
-
const
|
|
26
|
+
const PACKAGE_NAME = "react-native-bootsplash";
|
|
27
|
+
let isExpo = false;
|
|
28
|
+
export const workingPath = process.env.INIT_CWD ?? process.env.PWD ?? process.cwd();
|
|
24
29
|
const projectRoot = findProjectRoot(workingPath);
|
|
25
|
-
const getIOSProjectConfig = getAppleProjectConfig({
|
|
26
|
-
platformName: "ios"
|
|
27
|
-
});
|
|
28
|
-
const ios = getIOSProjectConfig(projectRoot, {});
|
|
29
|
-
const android = getAndroidProjectConfig(projectRoot);
|
|
30
30
|
const promisifiedExec = util.promisify(childProcess.exec);
|
|
31
31
|
const exec = cmd => promisifiedExec(cmd).then(({
|
|
32
32
|
stdout,
|
|
@@ -34,16 +34,20 @@ const exec = cmd => promisifiedExec(cmd).then(({
|
|
|
34
34
|
}) => stdout || stderr);
|
|
35
35
|
export const log = {
|
|
36
36
|
error: text => {
|
|
37
|
-
console.log(pc.red(`❌ ${text}`));
|
|
37
|
+
console.log(pc.red(isExpo ? `❌ [${PACKAGE_NAME}] ${text}` : `❌ ${text}`));
|
|
38
38
|
},
|
|
39
39
|
title: (emoji, text) => {
|
|
40
|
-
|
|
40
|
+
if (!isExpo) {
|
|
41
|
+
console.log(`\n${emoji} ${pc.underline(pc.bold(text))}`);
|
|
42
|
+
}
|
|
41
43
|
},
|
|
42
44
|
warn: text => {
|
|
43
|
-
console.log(pc.yellow(`⚠️
|
|
45
|
+
console.log(pc.yellow(isExpo ? `⚠️ [${PACKAGE_NAME}] ${text}` : `⚠️ ${text}`));
|
|
44
46
|
},
|
|
45
47
|
write: (filePath, dimensions) => {
|
|
46
|
-
|
|
48
|
+
if (!isExpo) {
|
|
49
|
+
console.log(` ${path.relative(workingPath, filePath)}` + (dimensions != null ? ` (${dimensions.width}x${dimensions.height})` : ""));
|
|
50
|
+
}
|
|
47
51
|
}
|
|
48
52
|
};
|
|
49
53
|
const parseColor = value => {
|
|
@@ -64,19 +68,24 @@ const parseColor = value => {
|
|
|
64
68
|
};
|
|
65
69
|
};
|
|
66
70
|
const getStoryboard = ({
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
71
|
+
props,
|
|
72
|
+
extras
|
|
73
|
+
}) => {
|
|
74
|
+
const {
|
|
75
|
+
background,
|
|
76
|
+
logo
|
|
77
|
+
} = props;
|
|
78
|
+
const {
|
|
79
|
+
fileNameSuffix,
|
|
80
|
+
logoHeight
|
|
81
|
+
} = extras;
|
|
82
|
+
const {
|
|
70
83
|
R,
|
|
71
84
|
G,
|
|
72
85
|
B
|
|
73
|
-
}
|
|
74
|
-
fileNameSuffix
|
|
75
|
-
}) => {
|
|
86
|
+
} = background.rgb;
|
|
76
87
|
const frameWidth = 375;
|
|
77
88
|
const frameHeight = 667;
|
|
78
|
-
const logoX = (frameWidth - logoWidth) / 2;
|
|
79
|
-
const logoY = (frameHeight - logoHeight) / 2;
|
|
80
89
|
return dedent`
|
|
81
90
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
82
91
|
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="21701" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
|
|
@@ -98,7 +107,7 @@ const getStoryboard = ({
|
|
|
98
107
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
|
99
108
|
<subviews>
|
|
100
109
|
<imageView autoresizesSubviews="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" image="BootSplashLogo-${fileNameSuffix}" translatesAutoresizingMaskIntoConstraints="NO" id="3lX-Ut-9ad">
|
|
101
|
-
<rect key="frame" x="${
|
|
110
|
+
<rect key="frame" x="${(frameWidth - logo.width) / 2}" y="${(frameHeight - logoHeight) / 2}" width="${logo.width}" height="${logoHeight}"/>
|
|
102
111
|
<accessibility key="accessibilityConfiguration">
|
|
103
112
|
<accessibilityTraits key="traits" image="YES" notEnabled="YES"/>
|
|
104
113
|
</accessibility>
|
|
@@ -118,7 +127,7 @@ const getStoryboard = ({
|
|
|
118
127
|
</scene>
|
|
119
128
|
</scenes>
|
|
120
129
|
<resources>
|
|
121
|
-
<image name="BootSplashLogo-${fileNameSuffix}" width="${
|
|
130
|
+
<image name="BootSplashLogo-${fileNameSuffix}" width="${logo.width}" height="${logoHeight}"/>
|
|
122
131
|
<namedColor name="BootSplashBackground-${fileNameSuffix}">
|
|
123
132
|
<color red="${R}" green="${G}" blue="${B}" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
|
124
133
|
</namedColor>
|
|
@@ -140,20 +149,6 @@ export const hfs = {
|
|
|
140
149
|
recursive: true
|
|
141
150
|
}),
|
|
142
151
|
text: path => fs.readFileSync(path, "utf-8"),
|
|
143
|
-
copy: (src, dest) => {
|
|
144
|
-
if (hfs.isDir(src) || !hfs.exists(dest)) {
|
|
145
|
-
return fs.copySync(src, dest, {
|
|
146
|
-
overwrite: true
|
|
147
|
-
});
|
|
148
|
-
}
|
|
149
|
-
const srcBuffer = fs.readFileSync(src);
|
|
150
|
-
const destBuffer = fs.readFileSync(dest);
|
|
151
|
-
if (!srcBuffer.equals(destBuffer)) {
|
|
152
|
-
return fs.copySync(src, dest, {
|
|
153
|
-
overwrite: true
|
|
154
|
-
});
|
|
155
|
-
}
|
|
156
|
-
},
|
|
157
152
|
ensureDir: dir => {
|
|
158
153
|
fs.mkdirSync(dir, {
|
|
159
154
|
recursive: true
|
|
@@ -164,52 +159,6 @@ export const hfs = {
|
|
|
164
159
|
fs.writeFileSync(path, trimmed === "" ? trimmed : trimmed + "\n", "utf-8");
|
|
165
160
|
}
|
|
166
161
|
};
|
|
167
|
-
|
|
168
|
-
// Adapted from https://github.com/square/find-yarn-workspace-root
|
|
169
|
-
const findUp = (from, matcher) => {
|
|
170
|
-
let previous;
|
|
171
|
-
let current = path.normalize(from);
|
|
172
|
-
do {
|
|
173
|
-
const found = matcher(current);
|
|
174
|
-
if (typeof found !== "undefined") {
|
|
175
|
-
return found;
|
|
176
|
-
}
|
|
177
|
-
previous = current;
|
|
178
|
-
current = path.dirname(current);
|
|
179
|
-
} while (current !== previous);
|
|
180
|
-
};
|
|
181
|
-
export const getExpoConfig = from => {
|
|
182
|
-
const hasDependency = findUp(from, dir => {
|
|
183
|
-
const pkgPath = path.resolve(dir, "package.json");
|
|
184
|
-
if (fs.existsSync(pkgPath)) {
|
|
185
|
-
try {
|
|
186
|
-
const pkg = hfs.json(pkgPath);
|
|
187
|
-
return pkg.dependencies?.expo != null;
|
|
188
|
-
} catch {} // eslint-disable-line no-empty
|
|
189
|
-
}
|
|
190
|
-
}) ?? false;
|
|
191
|
-
if (!hasDependency) {
|
|
192
|
-
return {
|
|
193
|
-
isExpo: false
|
|
194
|
-
};
|
|
195
|
-
}
|
|
196
|
-
const version = findUp(from, dir => {
|
|
197
|
-
const pkgPath = path.resolve(dir, "node_modules", "expo", "package.json");
|
|
198
|
-
if (fs.existsSync(pkgPath)) {
|
|
199
|
-
try {
|
|
200
|
-
const pkg = hfs.json(pkgPath);
|
|
201
|
-
return pkg.version;
|
|
202
|
-
} catch {} // eslint-disable-line no-empty
|
|
203
|
-
}
|
|
204
|
-
});
|
|
205
|
-
if (version == null || semver.lt(version, "51.0.20")) {
|
|
206
|
-
log.error("Requires Expo 51.0.20 (or higher)");
|
|
207
|
-
process.exit(1);
|
|
208
|
-
}
|
|
209
|
-
return {
|
|
210
|
-
isExpo: true
|
|
211
|
-
};
|
|
212
|
-
};
|
|
213
162
|
export const writeJson = (filePath, content) => {
|
|
214
163
|
hfs.write(filePath, JSON.stringify(content, null, 2));
|
|
215
164
|
log.write(filePath);
|
|
@@ -262,48 +211,14 @@ export const writeXmlLike = async (filePath, content, {
|
|
|
262
211
|
log.write(filePath);
|
|
263
212
|
}
|
|
264
213
|
};
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
hfs.rm(file);
|
|
268
|
-
});
|
|
269
|
-
};
|
|
270
|
-
const getImageBase64 = async (image, width) => {
|
|
271
|
-
if (image == null) {
|
|
214
|
+
const getAssetBase64 = async (name, asset) => {
|
|
215
|
+
if (asset == null) {
|
|
272
216
|
return "";
|
|
273
217
|
}
|
|
274
|
-
const
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
};
|
|
279
|
-
const getFileNameSuffix = async ({
|
|
280
|
-
background,
|
|
281
|
-
brand,
|
|
282
|
-
brandWidth,
|
|
283
|
-
darkBackground,
|
|
284
|
-
darkBrand,
|
|
285
|
-
darkLogo,
|
|
286
|
-
logo,
|
|
287
|
-
logoWidth
|
|
288
|
-
}) => {
|
|
289
|
-
const [logoHash, darkLogoHash, brandHash, darkBrandHash] = await Promise.all([getImageBase64(logo, logoWidth), getImageBase64(darkLogo, logoWidth), getImageBase64(brand, brandWidth), getImageBase64(darkBrand, brandWidth)]);
|
|
290
|
-
const record = {
|
|
291
|
-
background: background.hex,
|
|
292
|
-
darkBackground: darkBackground?.hex ?? "",
|
|
293
|
-
logo: logoHash,
|
|
294
|
-
darkLogo: darkLogoHash,
|
|
295
|
-
brand: brandHash,
|
|
296
|
-
darkBrand: darkBrandHash
|
|
297
|
-
};
|
|
298
|
-
const stableKey = Object.keys(record).sort().map(key => record[key]).join();
|
|
299
|
-
return crypto.createHash("shake256", {
|
|
300
|
-
outputLength: 3
|
|
301
|
-
}).update(stableKey).digest("hex").toLowerCase();
|
|
302
|
-
};
|
|
303
|
-
const ensureSupportedFormat = async (name, image) => {
|
|
304
|
-
if (image == null) {
|
|
305
|
-
return;
|
|
306
|
-
}
|
|
218
|
+
const {
|
|
219
|
+
image,
|
|
220
|
+
width
|
|
221
|
+
} = asset;
|
|
307
222
|
const {
|
|
308
223
|
format
|
|
309
224
|
} = await image.metadata();
|
|
@@ -311,56 +226,32 @@ const ensureSupportedFormat = async (name, image) => {
|
|
|
311
226
|
log.error(`${name} image file format (${format}) is not supported`);
|
|
312
227
|
process.exit(1);
|
|
313
228
|
}
|
|
229
|
+
const buffer = await image.clone().resize(width).png({
|
|
230
|
+
quality: 100
|
|
231
|
+
}).toBuffer();
|
|
232
|
+
return buffer.toString("base64");
|
|
314
233
|
};
|
|
315
234
|
const getAndroidOutputPath = ({
|
|
316
|
-
assetsOutputPath,
|
|
317
|
-
brandHeight,
|
|
318
|
-
brandWidth,
|
|
319
235
|
flavor,
|
|
320
|
-
isExpo,
|
|
321
|
-
logoHeight,
|
|
322
|
-
logoWidth,
|
|
323
236
|
platforms
|
|
324
237
|
}) => {
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
}
|
|
328
|
-
const withSizeChecks = assetsOutputPath => {
|
|
329
|
-
if (logoWidth > 288 || logoHeight > 288) {
|
|
330
|
-
return log.warn("Logo size exceeding 288x288dp will be cropped by Android. Skipping Android assets generation…");
|
|
331
|
-
}
|
|
332
|
-
if (brandWidth > 200 || brandHeight > 80) {
|
|
333
|
-
return log.warn("Brand size exceeding 200x80dp will be cropped by Android. Skipping Android assets generation…");
|
|
334
|
-
}
|
|
335
|
-
if (logoWidth > 192 || logoHeight > 192) {
|
|
336
|
-
log.warn("Logo size exceeds 192x192dp. It might be cropped by Android.");
|
|
337
|
-
}
|
|
338
|
-
return assetsOutputPath;
|
|
339
|
-
};
|
|
340
|
-
if (isExpo) {
|
|
341
|
-
return withSizeChecks(path.resolve(assetsOutputPath, "android"));
|
|
342
|
-
}
|
|
343
|
-
if (android == null) {
|
|
238
|
+
const android = getAndroidProjectConfig(projectRoot);
|
|
239
|
+
if (!platforms.includes("android") || android == null) {
|
|
344
240
|
return;
|
|
345
241
|
}
|
|
346
242
|
const androidOutputPath = path.resolve(android.sourceDir, android.appName, "src", flavor, "res");
|
|
347
243
|
if (!hfs.exists(androidOutputPath)) {
|
|
348
244
|
return log.warn(`No ${path.relative(workingPath, androidOutputPath)} directory found. Skipping Android assets generation…`);
|
|
349
245
|
}
|
|
350
|
-
return
|
|
246
|
+
return androidOutputPath;
|
|
351
247
|
};
|
|
352
248
|
const getIOSOutputPath = ({
|
|
353
|
-
assetsOutputPath,
|
|
354
|
-
isExpo,
|
|
355
249
|
platforms
|
|
356
250
|
}) => {
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
}
|
|
360
|
-
if (
|
|
361
|
-
return path.resolve(assetsOutputPath, "ios");
|
|
362
|
-
}
|
|
363
|
-
if (ios == null) {
|
|
251
|
+
const ios = getAppleProjectConfig({
|
|
252
|
+
platformName: "ios"
|
|
253
|
+
})(projectRoot, {});
|
|
254
|
+
if (!platforms.includes("ios") || ios == null) {
|
|
364
255
|
return;
|
|
365
256
|
}
|
|
366
257
|
if (ios.xcodeProject == null) {
|
|
@@ -372,428 +263,386 @@ const getIOSOutputPath = ({
|
|
|
372
263
|
}
|
|
373
264
|
return iosOutputPath;
|
|
374
265
|
};
|
|
375
|
-
const getHtmlTemplatePath =
|
|
376
|
-
isExpo,
|
|
266
|
+
const getHtmlTemplatePath = ({
|
|
377
267
|
html,
|
|
378
268
|
platforms
|
|
379
269
|
}) => {
|
|
380
270
|
if (!platforms.includes("web")) {
|
|
381
271
|
return;
|
|
382
272
|
}
|
|
383
|
-
if (isExpo) {
|
|
384
|
-
const htmlTemplatePath = path.resolve(workingPath, html);
|
|
385
|
-
const htmlTemplateRelativePath = path.relative(workingPath, htmlTemplatePath);
|
|
386
|
-
if (htmlTemplateRelativePath === "public/index.html" && !hfs.exists(htmlTemplatePath)) {
|
|
387
|
-
const cmd = `npx expo customize ${htmlTemplateRelativePath}`;
|
|
388
|
-
console.log(pc.dim(`Running ${cmd}`));
|
|
389
|
-
await exec(cmd);
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
273
|
const htmlTemplatePath = path.resolve(workingPath, html);
|
|
393
274
|
if (!hfs.exists(htmlTemplatePath)) {
|
|
394
|
-
return log.warn(`No ${path.relative(workingPath, htmlTemplatePath)}
|
|
275
|
+
return log.warn(`No ${path.relative(workingPath, htmlTemplatePath)} found. Skipping HTML + CSS generation…`);
|
|
395
276
|
}
|
|
396
277
|
return htmlTemplatePath;
|
|
397
278
|
};
|
|
398
|
-
const
|
|
399
|
-
|
|
279
|
+
const getInfoPlistPath = ({
|
|
280
|
+
iosOutputPath,
|
|
281
|
+
plist
|
|
282
|
+
}) => {
|
|
283
|
+
if (plist != null) {
|
|
284
|
+
const infoPlistPath = path.resolve(workingPath, plist);
|
|
285
|
+
if (!hfs.exists(infoPlistPath)) {
|
|
286
|
+
return log.warn(`No ${path.relative(workingPath, infoPlistPath)} found`);
|
|
287
|
+
}
|
|
288
|
+
return infoPlistPath;
|
|
289
|
+
}
|
|
290
|
+
return path.resolve(iosOutputPath, "Info.plist");
|
|
291
|
+
};
|
|
292
|
+
const getAssetHeight = asset => {
|
|
293
|
+
if (asset == null) {
|
|
400
294
|
return Promise.resolve(0);
|
|
401
295
|
}
|
|
402
|
-
return image.clone().resize(width).toBuffer().then(buffer => sharp(buffer).metadata()).then(({
|
|
296
|
+
return asset.image.clone().resize(asset.width).toBuffer().then(buffer => sharp(buffer).metadata()).then(({
|
|
403
297
|
height = 0
|
|
404
298
|
}) => Math.round(height));
|
|
405
299
|
};
|
|
406
|
-
const
|
|
407
|
-
|
|
408
|
-
return require("./addon"); // eslint-disable-line
|
|
409
|
-
} catch {
|
|
410
|
-
return;
|
|
411
|
-
}
|
|
412
|
-
};
|
|
413
|
-
export const generate = async ({
|
|
414
|
-
projectType,
|
|
415
|
-
platforms,
|
|
416
|
-
html,
|
|
417
|
-
flavor,
|
|
300
|
+
const getProps = ({
|
|
301
|
+
android = {},
|
|
418
302
|
licenseKey,
|
|
419
|
-
...
|
|
303
|
+
...config
|
|
420
304
|
}) => {
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
log.error("Requires Node 18 (or higher)");
|
|
305
|
+
if (semver.lt(process.versions.node, "20.0.0")) {
|
|
306
|
+
log.error("Requires Node 20 (or higher)");
|
|
424
307
|
process.exit(1);
|
|
425
308
|
}
|
|
426
|
-
const
|
|
427
|
-
const
|
|
428
|
-
const
|
|
429
|
-
const
|
|
430
|
-
const
|
|
431
|
-
const
|
|
432
|
-
const
|
|
433
|
-
const
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
const
|
|
309
|
+
const assetsOutputPath = path.resolve(workingPath, config.assetsOutput);
|
|
310
|
+
const logoPath = path.resolve(workingPath, config.logo);
|
|
311
|
+
const darkLogoPath = config.darkLogo != null ? path.resolve(workingPath, config.darkLogo) : undefined;
|
|
312
|
+
const brandPath = config.brand != null ? path.resolve(workingPath, config.brand) : undefined;
|
|
313
|
+
const darkBrandPath = config.darkBrand != null ? path.resolve(workingPath, config.darkBrand) : undefined;
|
|
314
|
+
const logoWidth = config.logoWidth - config.logoWidth % 2;
|
|
315
|
+
const brandWidth = config.brandWidth - config.brandWidth % 2;
|
|
316
|
+
const logo = {
|
|
317
|
+
path: logoPath,
|
|
318
|
+
image: sharp(logoPath),
|
|
319
|
+
width: logoWidth
|
|
320
|
+
};
|
|
321
|
+
const darkLogo = darkLogoPath != null ? {
|
|
322
|
+
path: darkLogoPath,
|
|
323
|
+
image: sharp(darkLogoPath),
|
|
324
|
+
width: logoWidth
|
|
325
|
+
} : undefined;
|
|
326
|
+
const brand = brandPath != null ? {
|
|
327
|
+
path: brandPath,
|
|
328
|
+
image: sharp(brandPath),
|
|
329
|
+
width: brandWidth
|
|
330
|
+
} : undefined;
|
|
331
|
+
const darkBrand = darkBrandPath != null ? {
|
|
332
|
+
path: darkBrandPath,
|
|
333
|
+
image: sharp(darkBrandPath),
|
|
334
|
+
width: brandWidth
|
|
335
|
+
} : undefined;
|
|
336
|
+
const background = parseColor(config.background);
|
|
337
|
+
const darkBackground = config.darkBackground != null ? parseColor(config.darkBackground) : undefined;
|
|
439
338
|
const executeAddon = brand != null || darkBackground != null || darkLogo != null || darkBrand != null;
|
|
440
339
|
if (licenseKey != null && !executeAddon) {
|
|
441
340
|
log.warn("You specified a license key but none of the options that requires it.");
|
|
442
341
|
}
|
|
342
|
+
const optionNames = {
|
|
343
|
+
brand: isExpo ? "brand" : "--brand",
|
|
344
|
+
darkBackground: isExpo ? "darkBackground" : "--dark-background",
|
|
345
|
+
darkLogo: isExpo ? "darkLogo" : "--dark-logo",
|
|
346
|
+
darkBrand: isExpo ? "darkBrand" : "--dark-brand"
|
|
347
|
+
};
|
|
443
348
|
if (licenseKey == null && executeAddon) {
|
|
444
|
-
const options = [brand != null ?
|
|
349
|
+
const options = [brand != null ? optionNames.brand : "", darkBackground != null ? optionNames.darkBackground : "", darkLogo != null ? optionNames.darkLogo : "", darkBrand != null ? optionNames.darkBrand : ""].filter(option => option !== "").join(", ");
|
|
445
350
|
log.error(`You need to specify a license key in order to use ${options}.`);
|
|
446
351
|
process.exit(1);
|
|
447
352
|
}
|
|
448
353
|
if (brand == null && darkBrand != null) {
|
|
449
|
-
log.error(
|
|
354
|
+
log.error(`${optionNames.darkBrand} option couldn't be used without ${optionNames.brand}.`);
|
|
450
355
|
process.exit(1);
|
|
451
356
|
}
|
|
452
|
-
|
|
453
|
-
await ensureSupportedFormat("Dark logo", darkLogo);
|
|
454
|
-
await ensureSupportedFormat("Brand", brand);
|
|
455
|
-
await ensureSupportedFormat("Dark brand", darkBrand);
|
|
456
|
-
const logoHeight = await getImageHeight(logo, logoWidth);
|
|
457
|
-
const brandHeight = await getImageHeight(brand, brandWidth);
|
|
458
|
-
if (logoWidth < args.logoWidth) {
|
|
357
|
+
if (logoWidth < config.logoWidth) {
|
|
459
358
|
log.warn(`Logo width must be a multiple of 2. It has been rounded to ${logoWidth}dp.`);
|
|
460
359
|
}
|
|
461
|
-
if (brandWidth <
|
|
360
|
+
if (brandWidth < config.brandWidth) {
|
|
462
361
|
log.warn(`Brand width must be a multiple of 2. It has been rounded to ${brandWidth}dp.`);
|
|
463
362
|
}
|
|
464
|
-
|
|
363
|
+
return {
|
|
364
|
+
android,
|
|
365
|
+
assetsOutputPath,
|
|
366
|
+
licenseKey,
|
|
367
|
+
executeAddon,
|
|
465
368
|
background,
|
|
466
|
-
brand,
|
|
467
|
-
brandWidth,
|
|
468
369
|
darkBackground,
|
|
469
|
-
darkBrand,
|
|
470
|
-
darkLogo,
|
|
471
370
|
logo,
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
371
|
+
darkLogo,
|
|
372
|
+
brand,
|
|
373
|
+
darkBrand
|
|
374
|
+
};
|
|
375
|
+
};
|
|
376
|
+
export const getExtras = async ({
|
|
377
|
+
background,
|
|
378
|
+
darkBackground,
|
|
379
|
+
logo,
|
|
380
|
+
darkLogo,
|
|
381
|
+
brand,
|
|
382
|
+
darkBrand
|
|
383
|
+
}) => {
|
|
384
|
+
const [logoHash, darkLogoHash, brandHash, darkBrandHash, logoHeight, brandHeight] = await Promise.all([getAssetBase64("Logo", logo), getAssetBase64("Dark logo", darkLogo), getAssetBase64("Brand", brand), getAssetBase64("Dark brand", darkBrand), getAssetHeight(logo), getAssetHeight(brand)]);
|
|
385
|
+
const record = {
|
|
386
|
+
background: background.hex,
|
|
387
|
+
darkBackground: darkBackground?.hex ?? "",
|
|
388
|
+
logo: logoHash,
|
|
389
|
+
darkLogo: darkLogoHash,
|
|
390
|
+
brand: brandHash,
|
|
391
|
+
darkBrand: darkBrandHash
|
|
392
|
+
};
|
|
393
|
+
const stableKey = Object.keys(record).sort().map(key => record[key]).join();
|
|
394
|
+
const fileNameSuffix = crypto.createHash("shake256", {
|
|
395
|
+
outputLength: 3
|
|
396
|
+
}).update(stableKey).digest("hex").toLowerCase();
|
|
397
|
+
return {
|
|
398
|
+
fileNameSuffix,
|
|
480
399
|
logoHeight,
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
const
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
400
|
+
brandHeight
|
|
401
|
+
};
|
|
402
|
+
};
|
|
403
|
+
export const writeAndroidAssets = async ({
|
|
404
|
+
androidOutputPath,
|
|
405
|
+
props,
|
|
406
|
+
extras
|
|
407
|
+
}) => {
|
|
408
|
+
const {
|
|
409
|
+
logo,
|
|
410
|
+
brand
|
|
411
|
+
} = props;
|
|
412
|
+
if (logo.width > 192 || extras.logoHeight > 192) {
|
|
413
|
+
return log.warn("Logo size exceeding 192x192dp will be cropped by Android. Skipping Android assets generation…");
|
|
414
|
+
}
|
|
415
|
+
if (brand != null && (brand.width > 200 || extras.brandHeight > 80)) {
|
|
416
|
+
return log.warn("Brand size exceeding 200x80dp will be cropped by Android. Skipping Android assets generation…");
|
|
417
|
+
}
|
|
418
|
+
if (logo.width > 134 || extras.logoHeight > 134) {
|
|
419
|
+
log.warn("Logo size exceeds 134x134dp. It might be cropped by Android.");
|
|
420
|
+
}
|
|
421
|
+
log.title("🤖", "Android");
|
|
422
|
+
hfs.ensureDir(androidOutputPath);
|
|
423
|
+
await Promise.all([{
|
|
424
|
+
ratio: 1,
|
|
425
|
+
suffix: "mdpi"
|
|
426
|
+
}, {
|
|
427
|
+
ratio: 1.5,
|
|
428
|
+
suffix: "hdpi"
|
|
429
|
+
}, {
|
|
430
|
+
ratio: 2,
|
|
431
|
+
suffix: "xhdpi"
|
|
432
|
+
}, {
|
|
433
|
+
ratio: 3,
|
|
434
|
+
suffix: "xxhdpi"
|
|
435
|
+
}, {
|
|
436
|
+
ratio: 4,
|
|
437
|
+
suffix: "xxxhdpi"
|
|
438
|
+
}].map(({
|
|
439
|
+
ratio,
|
|
440
|
+
suffix
|
|
441
|
+
}) => {
|
|
442
|
+
const drawableDirPath = path.resolve(androidOutputPath, `drawable-${suffix}`);
|
|
443
|
+
hfs.ensureDir(drawableDirPath);
|
|
518
444
|
|
|
519
|
-
|
|
520
|
-
|
|
445
|
+
// https://developer.android.com/develop/ui/views/launch/splash-screen#dimensions
|
|
446
|
+
const canvasSize = 288 * ratio;
|
|
521
447
|
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
}
|
|
448
|
+
// https://sharp.pixelplumbing.com/api-constructor
|
|
449
|
+
const canvas = sharp({
|
|
450
|
+
create: {
|
|
451
|
+
width: canvasSize,
|
|
452
|
+
height: canvasSize,
|
|
453
|
+
channels: 4,
|
|
454
|
+
background: {
|
|
455
|
+
r: 255,
|
|
456
|
+
g: 255,
|
|
457
|
+
b: 255,
|
|
458
|
+
alpha: 0
|
|
534
459
|
}
|
|
535
|
-
});
|
|
536
|
-
const filePath = path.resolve(drawableDirPath, "bootsplash_logo.png");
|
|
537
|
-
return logo.clone().resize(logoWidth * ratio).toBuffer().then(input => canvas.composite([{
|
|
538
|
-
input
|
|
539
|
-
}]).png({
|
|
540
|
-
quality: 100
|
|
541
|
-
}).toFile(filePath)).then(() => {
|
|
542
|
-
log.write(filePath, {
|
|
543
|
-
width: canvasSize,
|
|
544
|
-
height: canvasSize
|
|
545
|
-
});
|
|
546
|
-
});
|
|
547
|
-
}));
|
|
548
|
-
if (!isExpo) {
|
|
549
|
-
const manifestXmlPath = path.resolve(androidOutputPath, "..", "AndroidManifest.xml");
|
|
550
|
-
if (hfs.exists(manifestXmlPath)) {
|
|
551
|
-
const manifestXml = readXmlLike(manifestXmlPath);
|
|
552
|
-
const activities = manifestXml.root.querySelectorAll("activity");
|
|
553
|
-
for (const activity of activities) {
|
|
554
|
-
if (activity.getAttribute("android:name") === ".MainActivity") {
|
|
555
|
-
activity.setAttribute("android:theme", "@style/BootTheme");
|
|
556
|
-
}
|
|
557
|
-
}
|
|
558
|
-
await writeXmlLike(manifestXmlPath, manifestXml.root.toString(), {
|
|
559
|
-
...manifestXml.formatOptions,
|
|
560
|
-
formatter: "prettier",
|
|
561
|
-
htmlWhitespaceSensitivity: "ignore",
|
|
562
|
-
selfClosingTags: true,
|
|
563
|
-
singleAttributePerLine: true
|
|
564
|
-
});
|
|
565
|
-
} else {
|
|
566
|
-
log.warn("No AndroidManifest.xml found. Skipping…");
|
|
567
|
-
}
|
|
568
|
-
const valuesPath = path.resolve(androidOutputPath, "values");
|
|
569
|
-
hfs.ensureDir(valuesPath);
|
|
570
|
-
const colorsXmlPath = path.resolve(valuesPath, "colors.xml");
|
|
571
|
-
const colorsXmlEntry = `<color name="bootsplash_background">${background.hex}</color>`;
|
|
572
|
-
if (hfs.exists(colorsXmlPath)) {
|
|
573
|
-
const colorsXml = readXmlLike(colorsXmlPath);
|
|
574
|
-
const nextColor = parseHtml(colorsXmlEntry);
|
|
575
|
-
const prevColor = colorsXml.root.querySelector('color[name="bootsplash_background"]');
|
|
576
|
-
if (prevColor != null) {
|
|
577
|
-
prevColor.replaceWith(nextColor);
|
|
578
|
-
} else {
|
|
579
|
-
colorsXml.root.querySelector("resources")?.appendChild(nextColor);
|
|
580
|
-
}
|
|
581
|
-
await writeXmlLike(colorsXmlPath, colorsXml.root.toString(), {
|
|
582
|
-
...colorsXml.formatOptions,
|
|
583
|
-
formatter: "xmlFormatter"
|
|
584
|
-
});
|
|
585
|
-
} else {
|
|
586
|
-
await writeXmlLike(colorsXmlPath, `<resources>${colorsXmlEntry}</resources>`, {
|
|
587
|
-
formatter: "xmlFormatter"
|
|
588
|
-
});
|
|
589
|
-
}
|
|
590
|
-
const stylesXmlPath = path.resolve(valuesPath, "styles.xml");
|
|
591
|
-
if (hfs.exists(stylesXmlPath)) {
|
|
592
|
-
const stylesXml = readXmlLike(stylesXmlPath);
|
|
593
|
-
const prevStyle = stylesXml.root.querySelector('style[name="BootTheme"]');
|
|
594
|
-
const parent = prevStyle?.getAttribute("parent") ?? "Theme.BootSplash";
|
|
595
|
-
const extraItems = parseHtml(prevStyle?.text.split("\n").map(line => line.trim()).join("") ?? "").childNodes.filter(node => {
|
|
596
|
-
if (!(node instanceof HTMLElement)) {
|
|
597
|
-
return true;
|
|
598
|
-
}
|
|
599
|
-
const name = node.getAttribute("name");
|
|
600
|
-
return name !== "bootSplashBackground" && name !== "bootSplashLogo" && name !== "bootSplashBrand" && name !== "postBootSplashTheme";
|
|
601
|
-
}).map(node => node.toString());
|
|
602
|
-
const styleItems = [...(extraItems.length > 0 ? [...extraItems, ""] : []), '<item name="bootSplashBackground">@color/bootsplash_background</item>', '<item name="bootSplashLogo">@drawable/bootsplash_logo</item>', ...(brand != null && brandPath != null ? ['<item name="bootSplashBrand">@drawable/bootsplash_brand</item>'] : []), '<item name="postBootSplashTheme">@style/AppTheme</item>'];
|
|
603
|
-
const nextStyle = parseHtml(dedent`
|
|
604
|
-
<style name="BootTheme" parent="${parent}">
|
|
605
|
-
${styleItems.join("\n")}
|
|
606
|
-
</style>
|
|
607
|
-
`);
|
|
608
|
-
prevStyle?.remove(); // remove the existing style
|
|
609
|
-
stylesXml.root.querySelector("resources")?.appendChild(nextStyle);
|
|
610
|
-
await writeXmlLike(stylesXmlPath, stylesXml.root.toString(), {
|
|
611
|
-
...stylesXml.formatOptions,
|
|
612
|
-
formatter: "prettier",
|
|
613
|
-
htmlWhitespaceSensitivity: "ignore"
|
|
614
|
-
});
|
|
615
|
-
} else {
|
|
616
|
-
log.warn("No styles.xml found. Skipping…");
|
|
617
460
|
}
|
|
618
|
-
}
|
|
619
|
-
}
|
|
620
|
-
if (iosOutputPath != null) {
|
|
621
|
-
log.title("🍏", "iOS");
|
|
622
|
-
hfs.ensureDir(iosOutputPath);
|
|
623
|
-
cleanIOSAssets(iosOutputPath);
|
|
624
|
-
const storyboardPath = path.resolve(iosOutputPath, "BootSplash.storyboard");
|
|
625
|
-
const colorsSetPath = path.resolve(iosOutputPath, "Colors.xcassets", `BootSplashBackground-${fileNameSuffix}.colorset`);
|
|
626
|
-
const imageSetPath = path.resolve(iosOutputPath, "Images.xcassets", `BootSplashLogo-${fileNameSuffix}.imageset`);
|
|
627
|
-
hfs.ensureDir(colorsSetPath);
|
|
628
|
-
hfs.ensureDir(imageSetPath);
|
|
629
|
-
await writeXmlLike(storyboardPath, getStoryboard({
|
|
630
|
-
logoHeight,
|
|
631
|
-
logoWidth,
|
|
632
|
-
background: background.rgb,
|
|
633
|
-
fileNameSuffix
|
|
634
|
-
}), {
|
|
635
|
-
formatter: "xmlFormatter",
|
|
636
|
-
whiteSpaceAtEndOfSelfclosingTag: false
|
|
637
461
|
});
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
}
|
|
649
|
-
}
|
|
650
|
-
}],
|
|
651
|
-
info: {
|
|
652
|
-
author: "xcode",
|
|
653
|
-
version: 1
|
|
654
|
-
}
|
|
462
|
+
const filePath = path.resolve(drawableDirPath, "bootsplash_logo.png");
|
|
463
|
+
return logo.image.clone().resize(logo.width * ratio).toBuffer().then(input => canvas.composite([{
|
|
464
|
+
input
|
|
465
|
+
}]).png({
|
|
466
|
+
quality: 100
|
|
467
|
+
}).toFile(filePath)).then(() => {
|
|
468
|
+
log.write(filePath, {
|
|
469
|
+
width: canvasSize,
|
|
470
|
+
height: canvasSize
|
|
471
|
+
});
|
|
655
472
|
});
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
473
|
+
}));
|
|
474
|
+
};
|
|
475
|
+
export const writeIOSAssets = async ({
|
|
476
|
+
iosOutputPath,
|
|
477
|
+
props,
|
|
478
|
+
extras
|
|
479
|
+
}) => {
|
|
480
|
+
const {
|
|
481
|
+
background,
|
|
482
|
+
logo
|
|
483
|
+
} = props;
|
|
484
|
+
log.title("🍏", "iOS");
|
|
485
|
+
hfs.ensureDir(iosOutputPath);
|
|
486
|
+
|
|
487
|
+
// clean existing assets
|
|
488
|
+
hfs.readDir(iosOutputPath).filter(file => file === "Colors.xcassets" || file === "Images.xcassets").map(file => path.join(iosOutputPath, file)).flatMap(dir => hfs.readDir(dir).filter(file => file.startsWith("BootSplash")).map(file => path.join(dir, file))).forEach(file => {
|
|
489
|
+
hfs.rm(file);
|
|
490
|
+
});
|
|
491
|
+
const storyboardPath = path.resolve(iosOutputPath, "BootSplash.storyboard");
|
|
492
|
+
await writeXmlLike(storyboardPath, getStoryboard({
|
|
493
|
+
props,
|
|
494
|
+
extras
|
|
495
|
+
}), {
|
|
496
|
+
formatter: "xmlFormatter",
|
|
497
|
+
whiteSpaceAtEndOfSelfclosingTag: false
|
|
498
|
+
});
|
|
499
|
+
const colorsSetPath = path.resolve(iosOutputPath, "Colors.xcassets", `BootSplashBackground-${extras.fileNameSuffix}.colorset`);
|
|
500
|
+
hfs.ensureDir(colorsSetPath);
|
|
501
|
+
writeJson(path.resolve(colorsSetPath, "Contents.json"), {
|
|
502
|
+
colors: [{
|
|
503
|
+
idiom: "universal",
|
|
504
|
+
color: {
|
|
505
|
+
"color-space": "srgb",
|
|
506
|
+
components: {
|
|
507
|
+
blue: background.rgb.B,
|
|
508
|
+
green: background.rgb.G,
|
|
509
|
+
red: background.rgb.R,
|
|
510
|
+
alpha: "1.000"
|
|
511
|
+
}
|
|
674
512
|
}
|
|
675
|
-
}
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
513
|
+
}],
|
|
514
|
+
info: {
|
|
515
|
+
author: "xcode",
|
|
516
|
+
version: 1
|
|
517
|
+
}
|
|
518
|
+
});
|
|
519
|
+
const logoFileName = `logo-${extras.fileNameSuffix}`;
|
|
520
|
+
const imagesSetPath = path.resolve(iosOutputPath, "Images.xcassets", `BootSplashLogo-${extras.fileNameSuffix}.imageset`);
|
|
521
|
+
hfs.ensureDir(imagesSetPath);
|
|
522
|
+
writeJson(path.resolve(imagesSetPath, "Contents.json"), {
|
|
523
|
+
images: [{
|
|
524
|
+
idiom: "universal",
|
|
525
|
+
filename: `${logoFileName}.png`,
|
|
526
|
+
scale: "1x"
|
|
679
527
|
}, {
|
|
680
|
-
|
|
681
|
-
|
|
528
|
+
idiom: "universal",
|
|
529
|
+
filename: `${logoFileName}@2x.png`,
|
|
530
|
+
scale: "2x"
|
|
682
531
|
}, {
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
532
|
+
idiom: "universal",
|
|
533
|
+
filename: `${logoFileName}@3x.png`,
|
|
534
|
+
scale: "3x"
|
|
535
|
+
}],
|
|
536
|
+
info: {
|
|
537
|
+
author: "xcode",
|
|
538
|
+
version: 1
|
|
539
|
+
}
|
|
540
|
+
});
|
|
541
|
+
await Promise.all([{
|
|
542
|
+
ratio: 1,
|
|
543
|
+
suffix: ""
|
|
544
|
+
}, {
|
|
545
|
+
ratio: 2,
|
|
546
|
+
suffix: "@2x"
|
|
547
|
+
}, {
|
|
548
|
+
ratio: 3,
|
|
549
|
+
suffix: "@3x"
|
|
550
|
+
}].map(({
|
|
551
|
+
ratio,
|
|
552
|
+
suffix
|
|
553
|
+
}) => {
|
|
554
|
+
const filePath = path.resolve(imagesSetPath, `${logoFileName}${suffix}.png`);
|
|
555
|
+
return logo.image.clone().resize(logo.width * ratio).png({
|
|
556
|
+
quality: 100
|
|
557
|
+
}).toFile(filePath).then(({
|
|
558
|
+
width,
|
|
559
|
+
height
|
|
688
560
|
}) => {
|
|
689
|
-
|
|
690
|
-
return logo.clone().resize(logoWidth * ratio).png({
|
|
691
|
-
quality: 100
|
|
692
|
-
}).toFile(filePath).then(({
|
|
561
|
+
log.write(filePath, {
|
|
693
562
|
width,
|
|
694
563
|
height
|
|
695
|
-
}) => {
|
|
696
|
-
log.write(filePath, {
|
|
697
|
-
width,
|
|
698
|
-
height
|
|
699
|
-
});
|
|
700
|
-
});
|
|
701
|
-
}));
|
|
702
|
-
if (!isExpo) {
|
|
703
|
-
const infoPlistPath = path.resolve(iosOutputPath, "Info.plist");
|
|
704
|
-
const infoPlist = plist.parse(hfs.text(infoPlistPath));
|
|
705
|
-
infoPlist["UILaunchStoryboardName"] = "BootSplash";
|
|
706
|
-
const formatted = formatXml(plist.build(infoPlist), {
|
|
707
|
-
collapseContent: true,
|
|
708
|
-
forceSelfClosingEmptyTag: false,
|
|
709
|
-
indentation: "\t",
|
|
710
|
-
lineSeparator: "\n",
|
|
711
|
-
whiteSpaceAtEndOfSelfclosingTag: false
|
|
712
|
-
}).replace(/<string\/>/gm, "<string></string>").replace(/^\t/gm, "");
|
|
713
|
-
hfs.write(infoPlistPath, formatted);
|
|
714
|
-
log.write(infoPlistPath);
|
|
715
|
-
const pbxprojectPath = Expo.IOSConfig.Paths.getPBXProjectPath(projectRoot);
|
|
716
|
-
const xcodeProjectPath = Expo.IOSConfig.Paths.getXcodeProjectPath(projectRoot);
|
|
717
|
-
const project = Expo.IOSConfig.XcodeUtils.getPbxproj(projectRoot);
|
|
718
|
-
const projectName = path.basename(iosOutputPath);
|
|
719
|
-
const groupName = path.parse(xcodeProjectPath).name;
|
|
720
|
-
Expo.IOSConfig.XcodeUtils.addResourceFileToGroup({
|
|
721
|
-
project,
|
|
722
|
-
filepath: path.join(projectName, "BootSplash.storyboard"),
|
|
723
|
-
groupName,
|
|
724
|
-
isBuildFile: true
|
|
725
|
-
});
|
|
726
|
-
Expo.IOSConfig.XcodeUtils.addResourceFileToGroup({
|
|
727
|
-
project,
|
|
728
|
-
filepath: path.join(projectName, "Colors.xcassets"),
|
|
729
|
-
groupName,
|
|
730
|
-
isBuildFile: true
|
|
731
564
|
});
|
|
732
|
-
hfs.write(pbxprojectPath, project.writeSync());
|
|
733
|
-
log.write(pbxprojectPath);
|
|
734
|
-
}
|
|
735
|
-
}
|
|
736
|
-
if (htmlTemplatePath != null) {
|
|
737
|
-
log.title("🌐", "Web");
|
|
738
|
-
const htmlTemplate = readXmlLike(htmlTemplatePath);
|
|
739
|
-
const {
|
|
740
|
-
format
|
|
741
|
-
} = await logo.metadata();
|
|
742
|
-
const prevStyle = htmlTemplate.root.querySelector("#bootsplash-style");
|
|
743
|
-
const base64 = (format === "svg" ? hfs.buffer(logoPath) : await logo.clone().resize(Math.round(logoWidth * 2)).png({
|
|
744
|
-
quality: 100
|
|
745
|
-
}).toBuffer()).toString("base64");
|
|
746
|
-
const dataURI = `data:image/${format ? "svg+xml" : "png"};base64,${base64}`;
|
|
747
|
-
const nextStyle = parseHtml(dedent`
|
|
748
|
-
<style id="bootsplash-style">
|
|
749
|
-
#bootsplash {
|
|
750
|
-
position: absolute;
|
|
751
|
-
top: 0;
|
|
752
|
-
bottom: 0;
|
|
753
|
-
left: 0;
|
|
754
|
-
right: 0;
|
|
755
|
-
overflow: hidden;
|
|
756
|
-
display: flex;
|
|
757
|
-
justify-content: center;
|
|
758
|
-
align-items: center;
|
|
759
|
-
background-color: ${background.hex};
|
|
760
|
-
}
|
|
761
|
-
#bootsplash-logo {
|
|
762
|
-
content: url("${dataURI}");
|
|
763
|
-
width: ${logoWidth}px;
|
|
764
|
-
height: ${logoHeight}px;
|
|
765
|
-
}
|
|
766
|
-
</style>
|
|
767
|
-
`);
|
|
768
|
-
if (prevStyle != null) {
|
|
769
|
-
prevStyle.replaceWith(nextStyle);
|
|
770
|
-
} else {
|
|
771
|
-
htmlTemplate.root.querySelector("head")?.appendChild(nextStyle);
|
|
772
|
-
}
|
|
773
|
-
const prevDiv = htmlTemplate.root.querySelector("#bootsplash");
|
|
774
|
-
const nextDiv = parseHtml(dedent`
|
|
775
|
-
<div id="bootsplash">
|
|
776
|
-
<div id="bootsplash-logo"></div>
|
|
777
|
-
</div>
|
|
778
|
-
`);
|
|
779
|
-
if (prevDiv != null) {
|
|
780
|
-
prevDiv.replaceWith(nextDiv);
|
|
781
|
-
} else {
|
|
782
|
-
htmlTemplate.root.querySelector("body")?.appendChild(nextDiv);
|
|
783
|
-
}
|
|
784
|
-
await writeXmlLike(htmlTemplatePath, htmlTemplate.root.toString(), {
|
|
785
|
-
...htmlTemplate.formatOptions,
|
|
786
|
-
formatter: "prettier",
|
|
787
|
-
useCssPlugin: true
|
|
788
565
|
});
|
|
566
|
+
}));
|
|
567
|
+
};
|
|
568
|
+
export const writeWebAssets = async ({
|
|
569
|
+
htmlTemplatePath,
|
|
570
|
+
props,
|
|
571
|
+
extras
|
|
572
|
+
}) => {
|
|
573
|
+
const {
|
|
574
|
+
background,
|
|
575
|
+
logo
|
|
576
|
+
} = props;
|
|
577
|
+
log.title("🌐", "Web");
|
|
578
|
+
const htmlTemplate = readXmlLike(htmlTemplatePath);
|
|
579
|
+
const {
|
|
580
|
+
format
|
|
581
|
+
} = await logo.image.metadata();
|
|
582
|
+
const prevStyle = htmlTemplate.root.querySelector("#bootsplash-style");
|
|
583
|
+
const base64 = (format === "svg" ? hfs.buffer(logo.path) : await logo.image.clone().resize(Math.round(logo.width * 2)).png({
|
|
584
|
+
quality: 100
|
|
585
|
+
}).toBuffer()).toString("base64");
|
|
586
|
+
const dataURI = `data:image/${format ? "svg+xml" : "png"};base64,${base64}`;
|
|
587
|
+
const nextStyle = parseHtml(dedent`
|
|
588
|
+
<style id="bootsplash-style">
|
|
589
|
+
#bootsplash {
|
|
590
|
+
position: absolute;
|
|
591
|
+
top: 0;
|
|
592
|
+
bottom: 0;
|
|
593
|
+
left: 0;
|
|
594
|
+
right: 0;
|
|
595
|
+
overflow: hidden;
|
|
596
|
+
display: flex;
|
|
597
|
+
justify-content: center;
|
|
598
|
+
align-items: center;
|
|
599
|
+
background-color: ${background.hex};
|
|
600
|
+
}
|
|
601
|
+
#bootsplash-logo {
|
|
602
|
+
content: url("${dataURI}");
|
|
603
|
+
width: ${logo.width}px;
|
|
604
|
+
height: ${extras.logoHeight}px;
|
|
605
|
+
}
|
|
606
|
+
</style>
|
|
607
|
+
`);
|
|
608
|
+
if (prevStyle != null) {
|
|
609
|
+
prevStyle.replaceWith(nextStyle);
|
|
610
|
+
} else {
|
|
611
|
+
htmlTemplate.root.querySelector("head")?.appendChild(nextStyle);
|
|
612
|
+
}
|
|
613
|
+
const prevDiv = htmlTemplate.root.querySelector("#bootsplash");
|
|
614
|
+
const nextDiv = parseHtml(dedent`
|
|
615
|
+
<div id="bootsplash">
|
|
616
|
+
<div id="bootsplash-logo"></div>
|
|
617
|
+
</div>
|
|
618
|
+
`);
|
|
619
|
+
if (prevDiv != null) {
|
|
620
|
+
prevDiv.replaceWith(nextDiv);
|
|
621
|
+
} else {
|
|
622
|
+
htmlTemplate.root.querySelector("body")?.appendChild(nextDiv);
|
|
789
623
|
}
|
|
624
|
+
await writeXmlLike(htmlTemplatePath, htmlTemplate.root.toString(), {
|
|
625
|
+
...htmlTemplate.formatOptions,
|
|
626
|
+
formatter: "prettier",
|
|
627
|
+
useCssPlugin: true
|
|
628
|
+
});
|
|
629
|
+
};
|
|
630
|
+
export const writeGenericAssets = async ({
|
|
631
|
+
props,
|
|
632
|
+
extras
|
|
633
|
+
}) => {
|
|
634
|
+
const {
|
|
635
|
+
assetsOutputPath,
|
|
636
|
+
background,
|
|
637
|
+
logo
|
|
638
|
+
} = props;
|
|
790
639
|
log.title("📄", "Assets");
|
|
791
640
|
hfs.ensureDir(assetsOutputPath);
|
|
792
641
|
writeJson(path.resolve(assetsOutputPath, "manifest.json"), {
|
|
793
642
|
background: background.hex,
|
|
794
643
|
logo: {
|
|
795
|
-
width:
|
|
796
|
-
height: logoHeight
|
|
644
|
+
width: logo.width,
|
|
645
|
+
height: extras.logoHeight
|
|
797
646
|
}
|
|
798
647
|
});
|
|
799
648
|
await Promise.all([{
|
|
@@ -816,7 +665,7 @@ export const generate = async ({
|
|
|
816
665
|
suffix
|
|
817
666
|
}) => {
|
|
818
667
|
const filePath = path.resolve(assetsOutputPath, `logo${suffix}.png`);
|
|
819
|
-
return logo.clone().resize(Math.round(
|
|
668
|
+
return logo.image.clone().resize(Math.round(logo.width * ratio)).png({
|
|
820
669
|
quality: 100
|
|
821
670
|
}).toFile(filePath).then(({
|
|
822
671
|
width,
|
|
@@ -828,30 +677,178 @@ export const generate = async ({
|
|
|
828
677
|
});
|
|
829
678
|
});
|
|
830
679
|
}));
|
|
680
|
+
};
|
|
681
|
+
const requireAddon = () => {
|
|
682
|
+
try {
|
|
683
|
+
return require("./addon");
|
|
684
|
+
} catch {
|
|
685
|
+
return;
|
|
686
|
+
}
|
|
687
|
+
};
|
|
688
|
+
export const generate = async ({
|
|
689
|
+
platforms,
|
|
690
|
+
html,
|
|
691
|
+
flavor,
|
|
692
|
+
plist,
|
|
693
|
+
...config
|
|
694
|
+
}) => {
|
|
695
|
+
const props = getProps(config);
|
|
696
|
+
const {
|
|
697
|
+
background,
|
|
698
|
+
brand,
|
|
699
|
+
licenseKey,
|
|
700
|
+
executeAddon
|
|
701
|
+
} = props;
|
|
702
|
+
const extras = await getExtras(props);
|
|
703
|
+
const androidOutputPath = getAndroidOutputPath({
|
|
704
|
+
flavor,
|
|
705
|
+
platforms
|
|
706
|
+
});
|
|
707
|
+
const iosOutputPath = getIOSOutputPath({
|
|
708
|
+
platforms
|
|
709
|
+
});
|
|
710
|
+
const htmlTemplatePath = getHtmlTemplatePath({
|
|
711
|
+
html,
|
|
712
|
+
platforms
|
|
713
|
+
});
|
|
714
|
+
if (androidOutputPath != null) {
|
|
715
|
+
await writeAndroidAssets({
|
|
716
|
+
androidOutputPath,
|
|
717
|
+
props,
|
|
718
|
+
extras
|
|
719
|
+
});
|
|
720
|
+
const manifestXmlPath = path.resolve(androidOutputPath, "..", "AndroidManifest.xml");
|
|
721
|
+
if (hfs.exists(manifestXmlPath)) {
|
|
722
|
+
const manifestXml = readXmlLike(manifestXmlPath);
|
|
723
|
+
const activities = manifestXml.root.querySelectorAll("activity");
|
|
724
|
+
for (const activity of activities) {
|
|
725
|
+
if (activity.getAttribute("android:name") === ".MainActivity") {
|
|
726
|
+
activity.setAttribute("android:theme", "@style/BootTheme");
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
await writeXmlLike(manifestXmlPath, manifestXml.root.toString(), {
|
|
730
|
+
...manifestXml.formatOptions,
|
|
731
|
+
formatter: "prettier",
|
|
732
|
+
htmlWhitespaceSensitivity: "ignore",
|
|
733
|
+
selfClosingTags: true,
|
|
734
|
+
singleAttributePerLine: true
|
|
735
|
+
});
|
|
736
|
+
} else {
|
|
737
|
+
log.warn("No AndroidManifest.xml found");
|
|
738
|
+
}
|
|
739
|
+
const valuesPath = path.resolve(androidOutputPath, "values");
|
|
740
|
+
hfs.ensureDir(valuesPath);
|
|
741
|
+
const colorsXmlPath = path.resolve(valuesPath, "colors.xml");
|
|
742
|
+
const colorsXmlEntry = `<color name="bootsplash_background">${background.hex}</color>`;
|
|
743
|
+
if (hfs.exists(colorsXmlPath)) {
|
|
744
|
+
const colorsXml = readXmlLike(colorsXmlPath);
|
|
745
|
+
const nextColor = parseHtml(colorsXmlEntry);
|
|
746
|
+
const prevColor = colorsXml.root.querySelector('color[name="bootsplash_background"]');
|
|
747
|
+
if (prevColor != null) {
|
|
748
|
+
prevColor.replaceWith(nextColor);
|
|
749
|
+
} else {
|
|
750
|
+
colorsXml.root.querySelector("resources")?.appendChild(nextColor);
|
|
751
|
+
}
|
|
752
|
+
await writeXmlLike(colorsXmlPath, colorsXml.root.toString(), {
|
|
753
|
+
...colorsXml.formatOptions,
|
|
754
|
+
formatter: "xmlFormatter"
|
|
755
|
+
});
|
|
756
|
+
} else {
|
|
757
|
+
await writeXmlLike(colorsXmlPath, `<resources>${colorsXmlEntry}</resources>`, {
|
|
758
|
+
formatter: "xmlFormatter"
|
|
759
|
+
});
|
|
760
|
+
}
|
|
761
|
+
const stylesXmlPath = path.resolve(valuesPath, "styles.xml");
|
|
762
|
+
if (hfs.exists(stylesXmlPath)) {
|
|
763
|
+
const stylesXml = readXmlLike(stylesXmlPath);
|
|
764
|
+
const prevStyle = stylesXml.root.querySelector('style[name="BootTheme"]');
|
|
765
|
+
const parent = prevStyle?.getAttribute("parent") ?? "Theme.BootSplash";
|
|
766
|
+
const extraItems = parseHtml(prevStyle?.text.split("\n").map(line => line.trim()).join("") ?? "").childNodes.filter(node => {
|
|
767
|
+
if (!(node instanceof HTMLElement)) {
|
|
768
|
+
return true;
|
|
769
|
+
}
|
|
770
|
+
const name = node.getAttribute("name");
|
|
771
|
+
return name !== "bootSplashBackground" && name !== "bootSplashLogo" && name !== "bootSplashBrand" && name !== "postBootSplashTheme";
|
|
772
|
+
}).map(node => node.toString());
|
|
773
|
+
const styleItems = [...(extraItems.length > 0 ? [...extraItems, ""] : []), '<item name="bootSplashBackground">@color/bootsplash_background</item>', '<item name="bootSplashLogo">@drawable/bootsplash_logo</item>', ...(brand != null ? ['<item name="bootSplashBrand">@drawable/bootsplash_brand</item>'] : []), '<item name="postBootSplashTheme">@style/AppTheme</item>'];
|
|
774
|
+
const nextStyle = parseHtml(dedent`
|
|
775
|
+
<style name="BootTheme" parent="${parent}">
|
|
776
|
+
${styleItems.join("\n")}
|
|
777
|
+
</style>
|
|
778
|
+
`);
|
|
779
|
+
prevStyle?.remove(); // remove the existing style
|
|
780
|
+
stylesXml.root.querySelector("resources")?.appendChild(nextStyle);
|
|
781
|
+
await writeXmlLike(stylesXmlPath, stylesXml.root.toString(), {
|
|
782
|
+
...stylesXml.formatOptions,
|
|
783
|
+
formatter: "prettier",
|
|
784
|
+
htmlWhitespaceSensitivity: "ignore"
|
|
785
|
+
});
|
|
786
|
+
} else {
|
|
787
|
+
log.warn("No styles.xml found");
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
if (iosOutputPath != null) {
|
|
791
|
+
await writeIOSAssets({
|
|
792
|
+
iosOutputPath,
|
|
793
|
+
props,
|
|
794
|
+
extras
|
|
795
|
+
});
|
|
796
|
+
const infoPlistPath = getInfoPlistPath({
|
|
797
|
+
iosOutputPath,
|
|
798
|
+
plist
|
|
799
|
+
});
|
|
800
|
+
if (infoPlistPath != null) {
|
|
801
|
+
const infoPlist = ExpoPlist.parse(hfs.text(infoPlistPath));
|
|
802
|
+
infoPlist["UILaunchStoryboardName"] = "BootSplash";
|
|
803
|
+
const formatted = formatXml(ExpoPlist.build(infoPlist), {
|
|
804
|
+
collapseContent: true,
|
|
805
|
+
forceSelfClosingEmptyTag: false,
|
|
806
|
+
indentation: "\t",
|
|
807
|
+
lineSeparator: "\n",
|
|
808
|
+
whiteSpaceAtEndOfSelfclosingTag: false
|
|
809
|
+
}).replace(/<string\/>/gm, "<string></string>").replace(/^\t/gm, "");
|
|
810
|
+
hfs.write(infoPlistPath, formatted);
|
|
811
|
+
log.write(infoPlistPath);
|
|
812
|
+
}
|
|
813
|
+
const pbxprojectPath = Expo.IOSConfig.Paths.getPBXProjectPath(projectRoot);
|
|
814
|
+
const xcodeProjectPath = Expo.IOSConfig.Paths.getXcodeProjectPath(projectRoot);
|
|
815
|
+
const project = Expo.IOSConfig.XcodeUtils.getPbxproj(projectRoot);
|
|
816
|
+
const projectName = path.basename(iosOutputPath);
|
|
817
|
+
const groupName = path.parse(xcodeProjectPath).name;
|
|
818
|
+
Expo.IOSConfig.XcodeUtils.addResourceFileToGroup({
|
|
819
|
+
project,
|
|
820
|
+
filepath: path.join(projectName, "BootSplash.storyboard"),
|
|
821
|
+
groupName,
|
|
822
|
+
isBuildFile: true
|
|
823
|
+
});
|
|
824
|
+
Expo.IOSConfig.XcodeUtils.addResourceFileToGroup({
|
|
825
|
+
project,
|
|
826
|
+
filepath: path.join(projectName, "Colors.xcassets"),
|
|
827
|
+
groupName,
|
|
828
|
+
isBuildFile: true
|
|
829
|
+
});
|
|
830
|
+
hfs.write(pbxprojectPath, project.writeSync());
|
|
831
|
+
log.write(pbxprojectPath);
|
|
832
|
+
}
|
|
833
|
+
if (htmlTemplatePath != null) {
|
|
834
|
+
await writeWebAssets({
|
|
835
|
+
htmlTemplatePath,
|
|
836
|
+
props,
|
|
837
|
+
extras
|
|
838
|
+
});
|
|
839
|
+
}
|
|
840
|
+
await writeGenericAssets({
|
|
841
|
+
props,
|
|
842
|
+
extras
|
|
843
|
+
});
|
|
831
844
|
if (licenseKey != null && executeAddon) {
|
|
832
845
|
const addon = requireAddon();
|
|
833
846
|
await addon?.execute({
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
fileNameSuffix,
|
|
847
|
+
props,
|
|
848
|
+
extras,
|
|
837
849
|
androidOutputPath,
|
|
838
850
|
iosOutputPath,
|
|
839
|
-
htmlTemplatePath
|
|
840
|
-
assetsOutputPath,
|
|
841
|
-
logoHeight,
|
|
842
|
-
logoWidth,
|
|
843
|
-
brandHeight,
|
|
844
|
-
brandWidth,
|
|
845
|
-
logoPath,
|
|
846
|
-
darkLogoPath,
|
|
847
|
-
brandPath,
|
|
848
|
-
darkBrandPath,
|
|
849
|
-
background,
|
|
850
|
-
logo,
|
|
851
|
-
brand,
|
|
852
|
-
darkBackground,
|
|
853
|
-
darkLogo,
|
|
854
|
-
darkBrand
|
|
851
|
+
htmlTemplatePath
|
|
855
852
|
});
|
|
856
853
|
} else {
|
|
857
854
|
console.log(`
|
|
@@ -862,4 +859,275 @@ ${pc.blue("┗━━━━━━━━━━━━━━━━━━━━━━
|
|
|
862
859
|
}
|
|
863
860
|
console.log(`\n💖 Thanks for using ${pc.underline("react-native-bootsplash")}`);
|
|
864
861
|
};
|
|
862
|
+
|
|
863
|
+
// Expo plugin
|
|
864
|
+
|
|
865
|
+
const withoutExpoSplashScreen = Expo.createRunOncePlugin(config => config, "expo-splash-screen", "skip");
|
|
866
|
+
const withAndroidAssets = (config, props) => Expo.withDangerousMod(config, ["android", async config => {
|
|
867
|
+
const {
|
|
868
|
+
platformProjectRoot
|
|
869
|
+
} = config.modRequest;
|
|
870
|
+
const extras = await getExtras(props);
|
|
871
|
+
const androidOutputPath = path.resolve(platformProjectRoot, "app", "src", "main", "res");
|
|
872
|
+
await writeAndroidAssets({
|
|
873
|
+
androidOutputPath,
|
|
874
|
+
props,
|
|
875
|
+
extras
|
|
876
|
+
});
|
|
877
|
+
return config;
|
|
878
|
+
}]);
|
|
879
|
+
const withAndroidManifest = config => Expo.withAndroidManifest(config, config => {
|
|
880
|
+
config.modResults.manifest.application?.forEach(application => {
|
|
881
|
+
if (application.$["android:name"] === ".MainApplication") {
|
|
882
|
+
const {
|
|
883
|
+
activity
|
|
884
|
+
} = application;
|
|
885
|
+
activity?.forEach(activity => {
|
|
886
|
+
if (activity.$["android:name"] === ".MainActivity") {
|
|
887
|
+
activity.$["android:theme"] = "@style/BootTheme";
|
|
888
|
+
}
|
|
889
|
+
});
|
|
890
|
+
}
|
|
891
|
+
});
|
|
892
|
+
return config;
|
|
893
|
+
});
|
|
894
|
+
const withMainActivity = config => Expo.withMainActivity(config, config => {
|
|
895
|
+
const {
|
|
896
|
+
modResults
|
|
897
|
+
} = config;
|
|
898
|
+
const withImports = addImports(modResults.contents.replace(/(\/\/ )?setTheme\(R\.style\.AppTheme\)/, "// setTheme(R.style.AppTheme)"), ["android.os.Bundle", "com.zoontek.rnbootsplash.RNBootSplash"], false);
|
|
899
|
+
|
|
900
|
+
// indented with 4 spaces
|
|
901
|
+
const withInit = mergeContents({
|
|
902
|
+
src: withImports,
|
|
903
|
+
comment: " //",
|
|
904
|
+
tag: "bootsplash-init",
|
|
905
|
+
offset: 0,
|
|
906
|
+
anchor: /super\.onCreate\(null\)/,
|
|
907
|
+
newSrc: " RNBootSplash.init(this, R.style.BootTheme)"
|
|
908
|
+
});
|
|
909
|
+
return {
|
|
910
|
+
...config,
|
|
911
|
+
modResults: {
|
|
912
|
+
...modResults,
|
|
913
|
+
contents: withInit.contents
|
|
914
|
+
}
|
|
915
|
+
};
|
|
916
|
+
});
|
|
917
|
+
const withAndroidStyles = (config, props) => Expo.withAndroidStyles(config, config => {
|
|
918
|
+
const {
|
|
919
|
+
android,
|
|
920
|
+
brand
|
|
921
|
+
} = props;
|
|
922
|
+
const {
|
|
923
|
+
darkContentBarsStyle
|
|
924
|
+
} = android;
|
|
925
|
+
const {
|
|
926
|
+
modResults
|
|
927
|
+
} = config;
|
|
928
|
+
const {
|
|
929
|
+
resources
|
|
930
|
+
} = modResults;
|
|
931
|
+
const {
|
|
932
|
+
style = []
|
|
933
|
+
} = resources;
|
|
934
|
+
const item = [{
|
|
935
|
+
$: {
|
|
936
|
+
name: "postBootSplashTheme"
|
|
937
|
+
},
|
|
938
|
+
_: "@style/AppTheme"
|
|
939
|
+
}, {
|
|
940
|
+
$: {
|
|
941
|
+
name: "bootSplashBackground"
|
|
942
|
+
},
|
|
943
|
+
_: "@color/bootsplash_background"
|
|
944
|
+
}, {
|
|
945
|
+
$: {
|
|
946
|
+
name: "bootSplashLogo"
|
|
947
|
+
},
|
|
948
|
+
_: "@drawable/bootsplash_logo"
|
|
949
|
+
}];
|
|
950
|
+
if (brand != null) {
|
|
951
|
+
item.push({
|
|
952
|
+
$: {
|
|
953
|
+
name: "bootSplashBrand"
|
|
954
|
+
},
|
|
955
|
+
_: "@drawable/bootsplash_brand"
|
|
956
|
+
});
|
|
957
|
+
}
|
|
958
|
+
if (darkContentBarsStyle != null) {
|
|
959
|
+
item.push({
|
|
960
|
+
$: {
|
|
961
|
+
name: "darkContentBarsStyle"
|
|
962
|
+
},
|
|
963
|
+
_: String(darkContentBarsStyle)
|
|
964
|
+
});
|
|
965
|
+
}
|
|
966
|
+
const withBootTheme = [...style.filter(({
|
|
967
|
+
$
|
|
968
|
+
}) => $.name !== "BootTheme"), {
|
|
969
|
+
$: {
|
|
970
|
+
name: "BootTheme",
|
|
971
|
+
parent: "Theme.BootSplash"
|
|
972
|
+
},
|
|
973
|
+
item
|
|
974
|
+
}];
|
|
975
|
+
return {
|
|
976
|
+
...config,
|
|
977
|
+
modResults: {
|
|
978
|
+
...modResults,
|
|
979
|
+
resources: {
|
|
980
|
+
...resources,
|
|
981
|
+
style: withBootTheme
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
};
|
|
985
|
+
});
|
|
986
|
+
const withAndroidColors = (config, props) => Expo.withAndroidColors(config, config => {
|
|
987
|
+
const {
|
|
988
|
+
background
|
|
989
|
+
} = props;
|
|
990
|
+
config.modResults = assignColorValue(config.modResults, {
|
|
991
|
+
name: "bootsplash_background",
|
|
992
|
+
value: background.hex
|
|
993
|
+
});
|
|
994
|
+
return config;
|
|
995
|
+
});
|
|
996
|
+
const withIOSAssets = (config, props) => Expo.withDangerousMod(config, ["ios", async config => {
|
|
997
|
+
const {
|
|
998
|
+
platformProjectRoot,
|
|
999
|
+
projectName = ""
|
|
1000
|
+
} = config.modRequest;
|
|
1001
|
+
const extras = await getExtras(props);
|
|
1002
|
+
const iosOutputPath = path.resolve(platformProjectRoot, projectName);
|
|
1003
|
+
await writeIOSAssets({
|
|
1004
|
+
iosOutputPath,
|
|
1005
|
+
props,
|
|
1006
|
+
extras
|
|
1007
|
+
});
|
|
1008
|
+
return config;
|
|
1009
|
+
}]);
|
|
1010
|
+
const withAppDelegate = config => Expo.withAppDelegate(config, config => {
|
|
1011
|
+
const {
|
|
1012
|
+
modResults
|
|
1013
|
+
} = config;
|
|
1014
|
+
const {
|
|
1015
|
+
language
|
|
1016
|
+
} = modResults;
|
|
1017
|
+
if (language !== "objc" && language !== "objcpp" && language !== "swift") {
|
|
1018
|
+
throw new Error(`Cannot modify the project AppDelegate as it's not in a supported language: ${language}`);
|
|
1019
|
+
}
|
|
1020
|
+
const withHeader = mergeContents({
|
|
1021
|
+
src: modResults.contents,
|
|
1022
|
+
comment: "//",
|
|
1023
|
+
tag: "bootsplash-header",
|
|
1024
|
+
offset: 1,
|
|
1025
|
+
anchor: /import Expo/,
|
|
1026
|
+
newSrc: "import RNBootSplash"
|
|
1027
|
+
});
|
|
1028
|
+
const withRootView = mergeContents({
|
|
1029
|
+
src: withHeader.contents,
|
|
1030
|
+
comment: "//",
|
|
1031
|
+
tag: "bootsplash-init",
|
|
1032
|
+
offset: 1,
|
|
1033
|
+
anchor: /class ReactNativeDelegate: ExpoReactNativeFactoryDelegate {/,
|
|
1034
|
+
newSrc: dedent`
|
|
1035
|
+
public override func customize(_ rootView: UIView) {
|
|
1036
|
+
super.customize(rootView)
|
|
1037
|
+
RNBootSplash.initWithStoryboard("BootSplash", rootView: rootView)
|
|
1038
|
+
}
|
|
1039
|
+
`
|
|
1040
|
+
});
|
|
1041
|
+
return {
|
|
1042
|
+
...config,
|
|
1043
|
+
modResults: {
|
|
1044
|
+
...modResults,
|
|
1045
|
+
contents: withRootView.contents
|
|
1046
|
+
}
|
|
1047
|
+
};
|
|
1048
|
+
});
|
|
1049
|
+
const withInfoPlist = config => Expo.withInfoPlist(config, config => {
|
|
1050
|
+
config.modResults["UILaunchStoryboardName"] = "BootSplash";
|
|
1051
|
+
return config;
|
|
1052
|
+
});
|
|
1053
|
+
const withXcodeProject = config => Expo.withXcodeProject(config, config => {
|
|
1054
|
+
const {
|
|
1055
|
+
projectName = ""
|
|
1056
|
+
} = config.modRequest;
|
|
1057
|
+
Expo.IOSConfig.XcodeUtils.addResourceFileToGroup({
|
|
1058
|
+
filepath: path.join(projectName, "BootSplash.storyboard"),
|
|
1059
|
+
groupName: projectName,
|
|
1060
|
+
project: config.modResults,
|
|
1061
|
+
isBuildFile: true
|
|
1062
|
+
});
|
|
1063
|
+
Expo.IOSConfig.XcodeUtils.addResourceFileToGroup({
|
|
1064
|
+
filepath: path.join(projectName, "Colors.xcassets"),
|
|
1065
|
+
groupName: projectName,
|
|
1066
|
+
project: config.modResults,
|
|
1067
|
+
isBuildFile: true
|
|
1068
|
+
});
|
|
1069
|
+
return config;
|
|
1070
|
+
});
|
|
1071
|
+
const withWebAssets = (config, props) => Expo.withDangerousMod(config, [config.platforms?.includes("ios") ? "ios" : "android", async config => {
|
|
1072
|
+
const extras = await getExtras(props);
|
|
1073
|
+
const fileName = "public/index.html";
|
|
1074
|
+
const htmlTemplatePath = path.resolve(workingPath, fileName);
|
|
1075
|
+
if (!hfs.exists(htmlTemplatePath)) {
|
|
1076
|
+
await exec(`npx expo customize ${fileName}`);
|
|
1077
|
+
}
|
|
1078
|
+
await writeWebAssets({
|
|
1079
|
+
htmlTemplatePath,
|
|
1080
|
+
props,
|
|
1081
|
+
extras
|
|
1082
|
+
});
|
|
1083
|
+
return config;
|
|
1084
|
+
}]);
|
|
1085
|
+
const withGenericAssets = (config, props) => Expo.withDangerousMod(config, [config.platforms?.includes("ios") ? "ios" : "android", async config => {
|
|
1086
|
+
const extras = await getExtras(props);
|
|
1087
|
+
await writeGenericAssets({
|
|
1088
|
+
props,
|
|
1089
|
+
extras
|
|
1090
|
+
});
|
|
1091
|
+
return config;
|
|
1092
|
+
}]);
|
|
1093
|
+
export const withBootSplash = Expo.createRunOncePlugin((config, baseProps) => {
|
|
1094
|
+
const {
|
|
1095
|
+
platforms = [],
|
|
1096
|
+
sdkVersion = "0.1.0"
|
|
1097
|
+
} = config;
|
|
1098
|
+
isExpo = true;
|
|
1099
|
+
if (semver.lt(sdkVersion, "53.0.0")) {
|
|
1100
|
+
log.error("Requires Expo 53.0.0 (or higher)");
|
|
1101
|
+
process.exit(1);
|
|
1102
|
+
}
|
|
1103
|
+
if (!baseProps?.logo) {
|
|
1104
|
+
log.error("Missing required parameter 'logo'");
|
|
1105
|
+
process.exit(1);
|
|
1106
|
+
}
|
|
1107
|
+
const props = getProps({
|
|
1108
|
+
assetsOutput: "assets/bootsplash",
|
|
1109
|
+
background: "#fff",
|
|
1110
|
+
brandWidth: 80,
|
|
1111
|
+
logoWidth: 100,
|
|
1112
|
+
...baseProps
|
|
1113
|
+
});
|
|
1114
|
+
const {
|
|
1115
|
+
executeAddon
|
|
1116
|
+
} = props;
|
|
1117
|
+
const addon = executeAddon ? requireAddon() : undefined;
|
|
1118
|
+
const plugins = [withoutExpoSplashScreen, addon?.withGenericAssets ?? withGenericAssets];
|
|
1119
|
+
if (platforms.includes("android")) {
|
|
1120
|
+
plugins.push(addon?.withAndroidAssets ?? withAndroidAssets, withAndroidManifest, withMainActivity, withAndroidStyles, withAndroidColors);
|
|
1121
|
+
if (addon != null) {
|
|
1122
|
+
plugins.push(addon.withAndroidColorsNight);
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1125
|
+
if (platforms.includes("ios")) {
|
|
1126
|
+
plugins.push(addon?.withIOSAssets ?? withIOSAssets, withAppDelegate, withInfoPlist, withXcodeProject);
|
|
1127
|
+
}
|
|
1128
|
+
if (platforms.includes("web")) {
|
|
1129
|
+
plugins.push(addon?.withWebAssets ?? withWebAssets);
|
|
1130
|
+
}
|
|
1131
|
+
return Expo.withPlugins(config, plugins.map(plugin => [plugin, props]));
|
|
1132
|
+
}, PACKAGE_NAME);
|
|
865
1133
|
//# sourceMappingURL=generate.js.map
|