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