react-native-bootsplash 7.0.0-beta.0 → 7.0.0-beta.2

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/src/generate.ts CHANGED
@@ -1,12 +1,8 @@
1
1
  import * as Expo from "@expo/config-plugins";
2
- import { assignColorValue } from "@expo/config-plugins/build/android/Colors";
3
- import { addImports } from "@expo/config-plugins/build/android/codeMod";
4
- import { mergeContents } from "@expo/config-plugins/build/utils/generateCode";
5
2
  import ExpoPlist from "@expo/plist";
6
3
  import { projectConfig as getAndroidProjectConfig } from "@react-native-community/cli-config-android";
7
4
  import { getProjectConfig as getAppleProjectConfig } from "@react-native-community/cli-config-apple";
8
5
  import { findProjectRoot } from "@react-native-community/cli-tools";
9
- import childProcess from "child_process";
10
6
  import crypto from "crypto";
11
7
  import detectIndent, { type Indent } from "detect-indent";
12
8
  import fs from "fs-extra";
@@ -20,22 +16,12 @@ import * as prettier from "prettier/standalone";
20
16
  import semver from "semver";
21
17
  import sharp, { type Sharp } from "sharp";
22
18
  import { dedent } from "ts-dedent";
23
- import util from "util";
24
19
  import formatXml, { type XMLFormatterOptions } from "xml-formatter";
25
20
  import type { Manifest } from ".";
26
21
 
27
- const PACKAGE_NAME = "react-native-bootsplash";
22
+ type Platforms = ("android" | "ios" | "web")[];
28
23
 
29
- let isExpo = false;
30
-
31
- export const workingPath =
32
- process.env.INIT_CWD ?? process.env.PWD ?? process.cwd();
33
-
34
- const projectRoot = findProjectRoot(workingPath);
35
-
36
- export type Platforms = ("android" | "ios" | "web")[];
37
-
38
- export type RGBColor = {
24
+ type RGBColor = {
39
25
  R: string;
40
26
  G: string;
41
27
  B: string;
@@ -46,16 +32,19 @@ type Color = {
46
32
  rgb: RGBColor;
47
33
  };
48
34
 
49
- const promisifiedExec = util.promisify(childProcess.exec);
35
+ export const packageName = "react-native-bootsplash";
36
+ const workingPath = process.env.INIT_CWD ?? process.env.PWD ?? process.cwd();
37
+ const projectRoot = findProjectRoot(workingPath);
50
38
 
51
- const exec = (cmd: string) =>
52
- promisifiedExec(cmd).then(({ stdout, stderr }) => stdout || stderr);
39
+ let isExpo = false;
40
+
41
+ export const setIsExpo = (value: boolean) => {
42
+ isExpo = value;
43
+ };
53
44
 
54
45
  export const log = {
55
46
  error: (text: string) => {
56
- console.log(
57
- pc.red(isExpo ? `❌ [${PACKAGE_NAME}] ${text}` : `❌ ${text}`),
58
- );
47
+ console.log(pc.red(isExpo ? `❌ [${packageName}] ${text}` : `❌ ${text}`));
59
48
  },
60
49
  title: (emoji: string, text: string) => {
61
50
  if (!isExpo) {
@@ -64,7 +53,7 @@ export const log = {
64
53
  },
65
54
  warn: (text: string) => {
66
55
  console.log(
67
- pc.yellow(isExpo ? `⚠️ [${PACKAGE_NAME}] ${text}` : `⚠️ ${text}`),
56
+ pc.yellow(isExpo ? `⚠️ [${packageName}] ${text}` : `⚠️ ${text}`),
68
57
  );
69
58
  },
70
59
  write: (filePath: string, dimensions?: { width: number; height: number }) => {
@@ -101,9 +90,8 @@ const parseColor = (value: string): Color => {
101
90
  return { hex: hex.toLowerCase(), rgb };
102
91
  };
103
92
 
104
- const getStoryboard = ({ props, extras }: { props: Props; extras: Extras }) => {
105
- const { background, logo } = props;
106
- const { fileNameSuffix, logoHeight } = extras;
93
+ const getStoryboard = (props: Props) => {
94
+ const { background, logo, fileNameSuffix } = props;
107
95
 
108
96
  const { R, G, B } = background.rgb;
109
97
  const frameWidth = 375;
@@ -130,7 +118,7 @@ const getStoryboard = ({ props, extras }: { props: Props; extras: Extras }) => {
130
118
  <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
131
119
  <subviews>
132
120
  <imageView autoresizesSubviews="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" image="BootSplashLogo-${fileNameSuffix}" translatesAutoresizingMaskIntoConstraints="NO" id="3lX-Ut-9ad">
133
- <rect key="frame" x="${(frameWidth - logo.width) / 2}" y="${(frameHeight - logoHeight) / 2}" width="${logo.width}" height="${logoHeight}"/>
121
+ <rect key="frame" x="${(frameWidth - logo.width) / 2}" y="${(frameHeight - logo.height) / 2}" width="${logo.width}" height="${logo.height}"/>
134
122
  <accessibility key="accessibilityConfiguration">
135
123
  <accessibilityTraits key="traits" image="YES" notEnabled="YES"/>
136
124
  </accessibility>
@@ -150,7 +138,7 @@ const getStoryboard = ({ props, extras }: { props: Props; extras: Extras }) => {
150
138
  </scene>
151
139
  </scenes>
152
140
  <resources>
153
- <image name="BootSplashLogo-${fileNameSuffix}" width="${logo.width}" height="${logoHeight}"/>
141
+ <image name="BootSplashLogo-${fileNameSuffix}" width="${logo.width}" height="${logo.height}"/>
154
142
  <namedColor name="BootSplashBackground-${fileNameSuffix}">
155
143
  <color red="${R}" green="${G}" blue="${B}" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
156
144
  </namedColor>
@@ -255,37 +243,6 @@ export const writeXmlLike = async (
255
243
  }
256
244
  };
257
245
 
258
- export type Asset = {
259
- path: string;
260
- image: Sharp;
261
- width: number;
262
- };
263
-
264
- const getAssetBase64 = async (
265
- name: string,
266
- asset: Asset | undefined,
267
- ): Promise<string> => {
268
- if (asset == null) {
269
- return "";
270
- }
271
-
272
- const { image, width } = asset;
273
- const { format } = await image.metadata();
274
-
275
- if (format !== "png" && format !== "svg") {
276
- log.error(`${name} image file format (${format}) is not supported`);
277
- process.exit(1);
278
- }
279
-
280
- const buffer = await image
281
- .clone()
282
- .resize(width)
283
- .png({ quality: 100 })
284
- .toBuffer();
285
-
286
- return buffer.toString("base64");
287
- };
288
-
289
246
  const getAndroidOutputPath = ({
290
247
  flavor,
291
248
  platforms,
@@ -390,20 +347,51 @@ const getInfoPlistPath = ({
390
347
  return path.resolve(iosOutputPath, "Info.plist");
391
348
  };
392
349
 
393
- const getAssetHeight = (asset: Asset | undefined): Promise<number> => {
394
- if (asset == null) {
395
- return Promise.resolve(0);
350
+ export type Asset = {
351
+ path: string;
352
+ image: Sharp;
353
+ hash: string;
354
+ height: number;
355
+ width: number;
356
+ };
357
+
358
+ const toAsset = async (filePath: string, width: number): Promise<Asset> => {
359
+ const image = sharp(filePath);
360
+ const { format } = await image.metadata();
361
+
362
+ if (format !== "png" && format !== "svg") {
363
+ log.error(
364
+ `${path.basename(filePath)} image file format (${format}) is not supported`,
365
+ );
366
+ process.exit(1);
396
367
  }
397
368
 
398
- return asset.image
399
- .clone()
400
- .resize(asset.width)
401
- .toBuffer()
402
- .then((buffer) => sharp(buffer).metadata())
403
- .then(({ height = 0 }) => Math.round(height));
369
+ const [height, hash] = await Promise.all([
370
+ image
371
+ .clone()
372
+ .resize(width)
373
+ .toBuffer()
374
+ .then((buffer) => sharp(buffer).metadata())
375
+ .then(({ height = 0 }) => Math.round(height)),
376
+
377
+ image
378
+ .clone()
379
+ .resize(width)
380
+ .png({ quality: 100 })
381
+ .toBuffer()
382
+ .then((buffer) => buffer.toString("base64")),
383
+ ]);
384
+
385
+ return {
386
+ path: filePath,
387
+ image,
388
+ hash,
389
+ height,
390
+ width,
391
+ };
404
392
  };
405
393
 
406
- type Config = {
394
+ export type RawProps = {
407
395
  logo: string;
408
396
  background: string;
409
397
  logoWidth: number;
@@ -421,57 +409,46 @@ type Config = {
421
409
  };
422
410
  };
423
411
 
424
- const getProps = ({ android = {}, licenseKey, ...config }: Config) => {
412
+ export const transformProps = async (
413
+ rootPath: string,
414
+ { android = {}, licenseKey, ...rawProps }: RawProps,
415
+ ) => {
425
416
  if (semver.lt(process.versions.node, "20.0.0")) {
426
417
  log.error("Requires Node 20 (or higher)");
427
418
  process.exit(1);
428
419
  }
429
420
 
430
- const assetsOutputPath = path.resolve(workingPath, config.assetsOutput);
431
- const logoPath = path.resolve(workingPath, config.logo);
421
+ const assetsOutputPath = path.resolve(rootPath, rawProps.assetsOutput);
422
+ const logoPath = path.resolve(rootPath, rawProps.logo);
432
423
 
433
424
  const darkLogoPath =
434
- config.darkLogo != null
435
- ? path.resolve(workingPath, config.darkLogo)
425
+ rawProps.darkLogo != null
426
+ ? path.resolve(rootPath, rawProps.darkLogo)
436
427
  : undefined;
437
428
 
438
429
  const brandPath =
439
- config.brand != null ? path.resolve(workingPath, config.brand) : undefined;
430
+ rawProps.brand != null ? path.resolve(rootPath, rawProps.brand) : undefined;
440
431
 
441
432
  const darkBrandPath =
442
- config.darkBrand != null
443
- ? path.resolve(workingPath, config.darkBrand)
444
- : undefined;
445
-
446
- const logoWidth = config.logoWidth - (config.logoWidth % 2);
447
- const brandWidth = config.brandWidth - (config.brandWidth % 2);
448
-
449
- const logo: Asset = {
450
- path: logoPath,
451
- image: sharp(logoPath),
452
- width: logoWidth,
453
- };
454
-
455
- const darkLogo: Asset | undefined =
456
- darkLogoPath != null
457
- ? { path: darkLogoPath, image: sharp(darkLogoPath), width: logoWidth }
433
+ rawProps.darkBrand != null
434
+ ? path.resolve(rootPath, rawProps.darkBrand)
458
435
  : undefined;
459
436
 
460
- const brand: Asset | undefined =
461
- brandPath != null
462
- ? { path: brandPath, image: sharp(brandPath), width: brandWidth }
463
- : undefined;
437
+ const logoWidth = rawProps.logoWidth - (rawProps.logoWidth % 2);
438
+ const brandWidth = rawProps.brandWidth - (rawProps.brandWidth % 2);
464
439
 
465
- const darkBrand: Asset | undefined =
466
- darkBrandPath != null
467
- ? { path: darkBrandPath, image: sharp(darkBrandPath), width: brandWidth }
468
- : undefined;
440
+ const [logo, darkLogo, brand, darkBrand] = await Promise.all([
441
+ toAsset(logoPath, logoWidth),
442
+ darkLogoPath != null ? toAsset(darkLogoPath, logoWidth) : undefined,
443
+ brandPath != null ? toAsset(brandPath, brandWidth) : undefined,
444
+ darkBrandPath != null ? toAsset(darkBrandPath, brandWidth) : undefined,
445
+ ]);
469
446
 
470
- const background = parseColor(config.background);
447
+ const background = parseColor(rawProps.background);
471
448
 
472
449
  const darkBackground =
473
- config.darkBackground != null
474
- ? parseColor(config.darkBackground)
450
+ rawProps.darkBackground != null
451
+ ? parseColor(rawProps.darkBackground)
475
452
  : undefined;
476
453
 
477
454
  const executeAddon =
@@ -515,66 +492,24 @@ const getProps = ({ android = {}, licenseKey, ...config }: Config) => {
515
492
  process.exit(1);
516
493
  }
517
494
 
518
- if (logoWidth < config.logoWidth) {
495
+ if (logoWidth < rawProps.logoWidth) {
519
496
  log.warn(
520
497
  `Logo width must be a multiple of 2. It has been rounded to ${logoWidth}dp.`,
521
498
  );
522
499
  }
523
- if (brandWidth < config.brandWidth) {
500
+ if (brandWidth < rawProps.brandWidth) {
524
501
  log.warn(
525
502
  `Brand width must be a multiple of 2. It has been rounded to ${brandWidth}dp.`,
526
503
  );
527
504
  }
528
505
 
529
- return {
530
- android,
531
- assetsOutputPath,
532
- licenseKey,
533
- executeAddon,
534
- background,
535
- darkBackground,
536
- logo,
537
- darkLogo,
538
- brand,
539
- darkBrand,
540
- };
541
- };
542
-
543
- export type Props = ReturnType<typeof getProps>;
544
-
545
- export const getExtras = async ({
546
- background,
547
- darkBackground,
548
- logo,
549
- darkLogo,
550
- brand,
551
- darkBrand,
552
- }: Props) => {
553
- const [
554
- logoHash,
555
- darkLogoHash,
556
- brandHash,
557
- darkBrandHash,
558
-
559
- logoHeight,
560
- brandHeight,
561
- ] = await Promise.all([
562
- getAssetBase64("Logo", logo),
563
- getAssetBase64("Dark logo", darkLogo),
564
- getAssetBase64("Brand", brand),
565
- getAssetBase64("Dark brand", darkBrand),
566
-
567
- getAssetHeight(logo),
568
- getAssetHeight(brand),
569
- ]);
570
-
571
506
  const record: Record<string, string> = {
572
507
  background: background.hex,
573
508
  darkBackground: darkBackground?.hex ?? "",
574
- logo: logoHash,
575
- darkLogo: darkLogoHash,
576
- brand: brandHash,
577
- darkBrand: darkBrandHash,
509
+ logo: logo.hash,
510
+ darkLogo: darkLogo?.hash ?? "",
511
+ brand: brand?.hash ?? "",
512
+ darkBrand: darkBrand?.hash ?? "",
578
513
  };
579
514
 
580
515
  const stableKey = Object.keys(record)
@@ -589,38 +524,44 @@ export const getExtras = async ({
589
524
  .toLowerCase();
590
525
 
591
526
  return {
527
+ android,
528
+ assetsOutputPath,
529
+ licenseKey,
530
+ executeAddon,
531
+ background,
532
+ darkBackground,
533
+ logo,
534
+ darkLogo,
535
+ brand,
536
+ darkBrand,
592
537
  fileNameSuffix,
593
- logoHeight,
594
- brandHeight,
595
538
  };
596
539
  };
597
540
 
598
- export type Extras = Awaited<ReturnType<typeof getExtras>>;
541
+ export type Props = Awaited<ReturnType<typeof transformProps>>;
599
542
 
600
543
  export const writeAndroidAssets = async ({
601
544
  androidOutputPath,
602
545
  props,
603
- extras,
604
546
  }: {
605
547
  androidOutputPath: string;
606
548
  props: Props;
607
- extras: Extras;
608
549
  }) => {
609
550
  const { logo, brand } = props;
610
551
 
611
- if (logo.width > 192 || extras.logoHeight > 192) {
552
+ if (logo.width > 192 || logo.height > 192) {
612
553
  return log.warn(
613
554
  "Logo size exceeding 192x192dp will be cropped by Android. Skipping Android assets generation…",
614
555
  );
615
556
  }
616
557
 
617
- if (brand != null && (brand.width > 200 || extras.brandHeight > 80)) {
558
+ if (brand != null && (brand.width > 200 || brand.height > 80)) {
618
559
  return log.warn(
619
560
  "Brand size exceeding 200x80dp will be cropped by Android. Skipping Android assets generation…",
620
561
  );
621
562
  }
622
563
 
623
- if (logo.width > 134 || extras.logoHeight > 134) {
564
+ if (logo.width > 134 || logo.height > 134) {
624
565
  log.warn("Logo size exceeds 134x134dp. It might be cropped by Android.");
625
566
  }
626
567
 
@@ -682,13 +623,11 @@ export const writeAndroidAssets = async ({
682
623
  export const writeIOSAssets = async ({
683
624
  iosOutputPath,
684
625
  props,
685
- extras,
686
626
  }: {
687
627
  iosOutputPath: string;
688
628
  props: Props;
689
- extras: Extras;
690
629
  }) => {
691
- const { background, logo } = props;
630
+ const { background, logo, fileNameSuffix } = props;
692
631
 
693
632
  log.title("🍏", "iOS");
694
633
  hfs.ensureDir(iosOutputPath);
@@ -710,7 +649,7 @@ export const writeIOSAssets = async ({
710
649
 
711
650
  const storyboardPath = path.resolve(iosOutputPath, "BootSplash.storyboard");
712
651
 
713
- await writeXmlLike(storyboardPath, getStoryboard({ props, extras }), {
652
+ await writeXmlLike(storyboardPath, getStoryboard(props), {
714
653
  formatter: "xmlFormatter",
715
654
  whiteSpaceAtEndOfSelfclosingTag: false,
716
655
  });
@@ -718,7 +657,7 @@ export const writeIOSAssets = async ({
718
657
  const colorsSetPath = path.resolve(
719
658
  iosOutputPath,
720
659
  "Colors.xcassets",
721
- `BootSplashBackground-${extras.fileNameSuffix}.colorset`,
660
+ `BootSplashBackground-${fileNameSuffix}.colorset`,
722
661
  );
723
662
 
724
663
  hfs.ensureDir(colorsSetPath);
@@ -744,12 +683,12 @@ export const writeIOSAssets = async ({
744
683
  },
745
684
  });
746
685
 
747
- const logoFileName = `logo-${extras.fileNameSuffix}`;
686
+ const logoFileName = `logo-${fileNameSuffix}`;
748
687
 
749
688
  const imagesSetPath = path.resolve(
750
689
  iosOutputPath,
751
690
  "Images.xcassets",
752
- `BootSplashLogo-${extras.fileNameSuffix}.imageset`,
691
+ `BootSplashLogo-${fileNameSuffix}.imageset`,
753
692
  );
754
693
 
755
694
  hfs.ensureDir(imagesSetPath);
@@ -804,11 +743,9 @@ export const writeIOSAssets = async ({
804
743
  export const writeWebAssets = async ({
805
744
  htmlTemplatePath,
806
745
  props,
807
- extras,
808
746
  }: {
809
747
  htmlTemplatePath: string;
810
748
  props: Props;
811
- extras: Extras;
812
749
  }) => {
813
750
  const { background, logo } = props;
814
751
 
@@ -847,7 +784,7 @@ export const writeWebAssets = async ({
847
784
  #bootsplash-logo {
848
785
  content: url("${dataURI}");
849
786
  width: ${logo.width}px;
850
- height: ${extras.logoHeight}px;
787
+ height: ${logo.height}px;
851
788
  }
852
789
  </style>
853
790
  `);
@@ -879,13 +816,7 @@ export const writeWebAssets = async ({
879
816
  });
880
817
  };
881
818
 
882
- export const writeGenericAssets = async ({
883
- props,
884
- extras,
885
- }: {
886
- props: Props;
887
- extras: Extras;
888
- }) => {
819
+ export const writeGenericAssets = async ({ props }: { props: Props }) => {
889
820
  const { assetsOutputPath, background, logo } = props;
890
821
 
891
822
  log.title("📄", "Assets");
@@ -895,7 +826,7 @@ export const writeGenericAssets = async ({
895
826
  background: background.hex,
896
827
  logo: {
897
828
  width: logo.width,
898
- height: extras.logoHeight,
829
+ height: logo.height,
899
830
  },
900
831
  } satisfies Manifest);
901
832
 
@@ -923,28 +854,48 @@ export const writeGenericAssets = async ({
923
854
 
924
855
  export type AddonConfig = {
925
856
  props: Props;
926
- extras: Extras;
927
-
928
857
  androidOutputPath: string | void;
929
858
  iosOutputPath: string | void;
930
859
  htmlTemplatePath: string | void;
931
860
  };
932
861
 
933
- const requireAddon = ():
862
+ export const requireAddon = ({
863
+ executeAddon,
864
+ licenseKey,
865
+ }: Props):
934
866
  | {
935
867
  execute: (config: AddonConfig) => Promise<void>;
936
868
 
937
- withAndroidAssets: Expo.ConfigPlugin<Props>;
938
- withAndroidColorsNight: Expo.ConfigPlugin<Props>;
939
- withIOSAssets: Expo.ConfigPlugin<Props>;
940
- withWebAssets: Expo.ConfigPlugin<Props>;
941
- withGenericAssets: Expo.ConfigPlugin<Props>;
869
+ writeAndroidAssets: (_: {
870
+ androidOutputPath: string;
871
+ props: Props;
872
+ }) => Promise<void>;
873
+
874
+ writeIOSAssets: (_: {
875
+ iosOutputPath: string;
876
+ props: Props;
877
+ }) => Promise<void>;
878
+
879
+ writeWebAssets: (_: {
880
+ htmlTemplatePath: string;
881
+ props: Props;
882
+ }) => Promise<void>;
883
+
884
+ writeGenericAssets: (_: { props: Props }) => Promise<void>;
885
+
886
+ withAndroidColorsNight: (_: {
887
+ config: Expo.ExportedConfigWithProps;
888
+ props: Props;
889
+ }) => Promise<Expo.ExportedConfigWithProps>;
942
890
  }
943
891
  | undefined => {
944
- try {
945
- return require("./addon");
946
- } catch {
947
- return;
892
+ if (licenseKey != null && executeAddon) {
893
+ try {
894
+ const addon = require("./dist/commonjs/addon");
895
+ return "default" in addon ? addon.default : addon;
896
+ } catch {
897
+ return;
898
+ }
948
899
  }
949
900
  };
950
901
 
@@ -953,7 +904,7 @@ export const generate = async ({
953
904
  html,
954
905
  flavor,
955
906
  plist,
956
- ...config
907
+ ...rawProps
957
908
  }: {
958
909
  platforms: Platforms;
959
910
  html: string;
@@ -971,16 +922,17 @@ export const generate = async ({
971
922
  darkLogo?: string;
972
923
  darkBrand?: string;
973
924
  }) => {
974
- const props = getProps(config);
975
- const { background, brand, licenseKey, executeAddon } = props;
976
- const extras = await getExtras(props);
925
+ const props = await transformProps(workingPath, rawProps);
926
+ const addon = requireAddon(props);
927
+
928
+ const { background, brand } = props;
977
929
 
978
930
  const androidOutputPath = getAndroidOutputPath({ flavor, platforms });
979
931
  const iosOutputPath = getIOSOutputPath({ platforms });
980
932
  const htmlTemplatePath = getHtmlTemplatePath({ html, platforms });
981
933
 
982
934
  if (androidOutputPath != null) {
983
- await writeAndroidAssets({ androidOutputPath, props, extras });
935
+ await writeAndroidAssets({ androidOutputPath, props });
984
936
 
985
937
  const manifestXmlPath = path.resolve(
986
938
  androidOutputPath,
@@ -1103,31 +1055,7 @@ export const generate = async ({
1103
1055
  }
1104
1056
 
1105
1057
  if (iosOutputPath != null) {
1106
- await writeIOSAssets({ iosOutputPath, props, extras });
1107
-
1108
- const infoPlistPath = getInfoPlistPath({ iosOutputPath, plist });
1109
-
1110
- if (infoPlistPath != null) {
1111
- const infoPlist = ExpoPlist.parse(hfs.text(infoPlistPath)) as Record<
1112
- string,
1113
- unknown
1114
- >;
1115
-
1116
- infoPlist["UILaunchStoryboardName"] = "BootSplash";
1117
-
1118
- const formatted = formatXml(ExpoPlist.build(infoPlist), {
1119
- collapseContent: true,
1120
- forceSelfClosingEmptyTag: false,
1121
- indentation: "\t",
1122
- lineSeparator: "\n",
1123
- whiteSpaceAtEndOfSelfclosingTag: false,
1124
- })
1125
- .replace(/<string\/>/gm, "<string></string>")
1126
- .replace(/^\t/gm, "");
1127
-
1128
- hfs.write(infoPlistPath, formatted);
1129
- log.write(infoPlistPath);
1130
- }
1058
+ await writeIOSAssets({ iosOutputPath, props });
1131
1059
 
1132
1060
  const pbxprojectPath = Expo.IOSConfig.Paths.getPBXProjectPath(projectRoot);
1133
1061
 
@@ -1154,21 +1082,41 @@ export const generate = async ({
1154
1082
 
1155
1083
  hfs.write(pbxprojectPath, project.writeSync());
1156
1084
  log.write(pbxprojectPath);
1085
+
1086
+ const infoPlistPath = getInfoPlistPath({ iosOutputPath, plist });
1087
+
1088
+ if (infoPlistPath != null) {
1089
+ const infoPlist = ExpoPlist.parse(hfs.text(infoPlistPath)) as Record<
1090
+ string,
1091
+ unknown
1092
+ >;
1093
+
1094
+ infoPlist["UILaunchStoryboardName"] = "BootSplash";
1095
+
1096
+ const formatted = formatXml(ExpoPlist.build(infoPlist), {
1097
+ collapseContent: true,
1098
+ forceSelfClosingEmptyTag: false,
1099
+ indentation: "\t",
1100
+ lineSeparator: "\n",
1101
+ whiteSpaceAtEndOfSelfclosingTag: false,
1102
+ })
1103
+ .replace(/<string\/>/gm, "<string></string>")
1104
+ .replace(/^\t/gm, "");
1105
+
1106
+ hfs.write(infoPlistPath, formatted);
1107
+ log.write(infoPlistPath);
1108
+ }
1157
1109
  }
1158
1110
 
1159
1111
  if (htmlTemplatePath != null) {
1160
- await writeWebAssets({ htmlTemplatePath, props, extras });
1112
+ await writeWebAssets({ htmlTemplatePath, props });
1161
1113
  }
1162
1114
 
1163
- await writeGenericAssets({ props, extras });
1115
+ await writeGenericAssets({ props });
1164
1116
 
1165
- if (licenseKey != null && executeAddon) {
1166
- const addon = requireAddon();
1167
-
1168
- await addon?.execute({
1117
+ if (addon != null) {
1118
+ await addon.execute({
1169
1119
  props,
1170
- extras,
1171
-
1172
1120
  androidOutputPath,
1173
1121
  iosOutputPath,
1174
1122
  htmlTemplatePath,
@@ -1189,342 +1137,3 @@ ${pc.blue("┗━━━━━━━━━━━━━━━━━━━━━━
1189
1137
  `\n💖 Thanks for using ${pc.underline("react-native-bootsplash")}`,
1190
1138
  );
1191
1139
  };
1192
-
1193
- // Expo plugin
1194
-
1195
- const withoutExpoSplashScreen: Expo.ConfigPlugin<Props> =
1196
- Expo.createRunOncePlugin((config) => config, "expo-splash-screen", "skip");
1197
-
1198
- const withAndroidAssets: Expo.ConfigPlugin<Props> = (config, props) =>
1199
- Expo.withDangerousMod(config, [
1200
- "android",
1201
- async (config) => {
1202
- const { platformProjectRoot } = config.modRequest;
1203
- const extras = await getExtras(props);
1204
-
1205
- const androidOutputPath = path.resolve(
1206
- platformProjectRoot,
1207
- "app",
1208
- "src",
1209
- "main",
1210
- "res",
1211
- );
1212
-
1213
- await writeAndroidAssets({ androidOutputPath, props, extras });
1214
- return config;
1215
- },
1216
- ]);
1217
-
1218
- const withAndroidManifest: Expo.ConfigPlugin<Props> = (config) =>
1219
- Expo.withAndroidManifest(config, (config) => {
1220
- config.modResults.manifest.application?.forEach((application) => {
1221
- if (application.$["android:name"] === ".MainApplication") {
1222
- const { activity } = application;
1223
-
1224
- activity?.forEach((activity) => {
1225
- if (activity.$["android:name"] === ".MainActivity") {
1226
- activity.$["android:theme"] = "@style/BootTheme";
1227
- }
1228
- });
1229
- }
1230
- });
1231
-
1232
- return config;
1233
- });
1234
-
1235
- const withMainActivity: Expo.ConfigPlugin<Props> = (config) =>
1236
- Expo.withMainActivity(config, (config) => {
1237
- const { modResults } = config;
1238
-
1239
- const withImports = addImports(
1240
- modResults.contents.replace(
1241
- /(\/\/ )?setTheme\(R\.style\.AppTheme\)/,
1242
- "// setTheme(R.style.AppTheme)",
1243
- ),
1244
- ["android.os.Bundle", "com.zoontek.rnbootsplash.RNBootSplash"],
1245
- false,
1246
- );
1247
-
1248
- // indented with 4 spaces
1249
- const withInit = mergeContents({
1250
- src: withImports,
1251
- comment: " //",
1252
- tag: "bootsplash-init",
1253
- offset: 0,
1254
- anchor: /super\.onCreate\(null\)/,
1255
- newSrc: " RNBootSplash.init(this, R.style.BootTheme)",
1256
- });
1257
-
1258
- return {
1259
- ...config,
1260
- modResults: {
1261
- ...modResults,
1262
- contents: withInit.contents,
1263
- },
1264
- };
1265
- });
1266
-
1267
- const withAndroidStyles: Expo.ConfigPlugin<Props> = (config, props) =>
1268
- Expo.withAndroidStyles(config, (config) => {
1269
- const { android, brand } = props;
1270
- const { darkContentBarsStyle } = android;
1271
-
1272
- const { modResults } = config;
1273
- const { resources } = modResults;
1274
- const { style = [] } = resources;
1275
-
1276
- const item = [
1277
- {
1278
- $: { name: "postBootSplashTheme" },
1279
- _: "@style/AppTheme",
1280
- },
1281
- {
1282
- $: { name: "bootSplashBackground" },
1283
- _: "@color/bootsplash_background",
1284
- },
1285
- {
1286
- $: { name: "bootSplashLogo" },
1287
- _: "@drawable/bootsplash_logo",
1288
- },
1289
- ];
1290
-
1291
- if (brand != null) {
1292
- item.push({
1293
- $: { name: "bootSplashBrand" },
1294
- _: "@drawable/bootsplash_brand",
1295
- });
1296
- }
1297
- if (darkContentBarsStyle != null) {
1298
- item.push({
1299
- $: { name: "darkContentBarsStyle" },
1300
- _: String(darkContentBarsStyle),
1301
- });
1302
- }
1303
-
1304
- const withBootTheme = [
1305
- ...style.filter(({ $ }) => $.name !== "BootTheme"),
1306
- {
1307
- $: {
1308
- name: "BootTheme",
1309
- parent: "Theme.BootSplash",
1310
- },
1311
- item,
1312
- },
1313
- ];
1314
-
1315
- return {
1316
- ...config,
1317
- modResults: {
1318
- ...modResults,
1319
- resources: {
1320
- ...resources,
1321
- style: withBootTheme,
1322
- },
1323
- },
1324
- };
1325
- });
1326
-
1327
- const withAndroidColors: Expo.ConfigPlugin<Props> = (config, props) =>
1328
- Expo.withAndroidColors(config, (config) => {
1329
- const { background } = props;
1330
-
1331
- config.modResults = assignColorValue(config.modResults, {
1332
- name: "bootsplash_background",
1333
- value: background.hex,
1334
- });
1335
-
1336
- return config;
1337
- });
1338
-
1339
- const withIOSAssets: Expo.ConfigPlugin<Props> = (config, props) =>
1340
- Expo.withDangerousMod(config, [
1341
- "ios",
1342
- async (config) => {
1343
- const { platformProjectRoot, projectName = "" } = config.modRequest;
1344
- const extras = await getExtras(props);
1345
- const iosOutputPath = path.resolve(platformProjectRoot, projectName);
1346
-
1347
- await writeIOSAssets({ iosOutputPath, props, extras });
1348
- return config;
1349
- },
1350
- ]);
1351
-
1352
- const withAppDelegate: Expo.ConfigPlugin<Props> = (config) =>
1353
- Expo.withAppDelegate(config, (config) => {
1354
- const { modResults } = config;
1355
- const { language } = modResults;
1356
-
1357
- if (language !== "objc" && language !== "objcpp" && language !== "swift") {
1358
- throw new Error(
1359
- `Cannot modify the project AppDelegate as it's not in a supported language: ${language}`,
1360
- );
1361
- }
1362
-
1363
- const withHeader = mergeContents({
1364
- src: modResults.contents,
1365
- comment: "//",
1366
- tag: "bootsplash-header",
1367
- offset: 1,
1368
- anchor: /import Expo/,
1369
- newSrc: "import RNBootSplash",
1370
- });
1371
-
1372
- const withRootView = mergeContents({
1373
- src: withHeader.contents,
1374
- comment: "//",
1375
- tag: "bootsplash-init",
1376
- offset: 1,
1377
- anchor: /class ReactNativeDelegate: ExpoReactNativeFactoryDelegate {/,
1378
- newSrc: dedent`
1379
- public override func customize(_ rootView: UIView) {
1380
- super.customize(rootView)
1381
- RNBootSplash.initWithStoryboard("BootSplash", rootView: rootView)
1382
- }
1383
- `,
1384
- });
1385
-
1386
- return {
1387
- ...config,
1388
- modResults: {
1389
- ...modResults,
1390
- contents: withRootView.contents,
1391
- },
1392
- };
1393
- });
1394
-
1395
- const withInfoPlist: Expo.ConfigPlugin<Props> = (config) =>
1396
- Expo.withInfoPlist(config, (config) => {
1397
- config.modResults["UILaunchStoryboardName"] = "BootSplash";
1398
- return config;
1399
- });
1400
-
1401
- const withXcodeProject: Expo.ConfigPlugin<Props> = (config) =>
1402
- Expo.withXcodeProject(config, (config) => {
1403
- const { projectName = "" } = config.modRequest;
1404
-
1405
- Expo.IOSConfig.XcodeUtils.addResourceFileToGroup({
1406
- filepath: path.join(projectName, "BootSplash.storyboard"),
1407
- groupName: projectName,
1408
- project: config.modResults,
1409
- isBuildFile: true,
1410
- });
1411
-
1412
- Expo.IOSConfig.XcodeUtils.addResourceFileToGroup({
1413
- filepath: path.join(projectName, "Colors.xcassets"),
1414
- groupName: projectName,
1415
- project: config.modResults,
1416
- isBuildFile: true,
1417
- });
1418
-
1419
- return config;
1420
- });
1421
-
1422
- const withWebAssets: Expo.ConfigPlugin<Props> = (config, props) =>
1423
- Expo.withDangerousMod(config, [
1424
- config.platforms?.includes("ios") ? "ios" : "android",
1425
- async (config) => {
1426
- const extras = await getExtras(props);
1427
- const fileName = "public/index.html";
1428
- const htmlTemplatePath = path.resolve(workingPath, fileName);
1429
-
1430
- if (!hfs.exists(htmlTemplatePath)) {
1431
- await exec(`npx expo customize ${fileName}`);
1432
- }
1433
-
1434
- await writeWebAssets({ htmlTemplatePath, props, extras });
1435
- return config;
1436
- },
1437
- ]);
1438
-
1439
- const withGenericAssets: Expo.ConfigPlugin<Props> = (config, props) =>
1440
- Expo.withDangerousMod(config, [
1441
- config.platforms?.includes("ios") ? "ios" : "android",
1442
- async (config) => {
1443
- const extras = await getExtras(props);
1444
- await writeGenericAssets({ props, extras });
1445
- return config;
1446
- },
1447
- ]);
1448
-
1449
- export const withBootSplash = Expo.createRunOncePlugin<
1450
- | {
1451
- logo: string;
1452
- background?: string;
1453
- logoWidth?: number;
1454
- assetsOutput?: string;
1455
-
1456
- licenseKey?: string;
1457
- brand?: string;
1458
- brandWidth?: number;
1459
- darkBackground?: string;
1460
- darkLogo?: string;
1461
- darkBrand?: string;
1462
-
1463
- android?: {
1464
- darkContentBarsStyle?: boolean;
1465
- };
1466
- }
1467
- | undefined
1468
- >((config, baseProps) => {
1469
- const { platforms = [], sdkVersion = "0.1.0" } = config;
1470
-
1471
- isExpo = true;
1472
-
1473
- if (semver.lt(sdkVersion, "53.0.0")) {
1474
- log.error("Requires Expo 53.0.0 (or higher)");
1475
- process.exit(1);
1476
- }
1477
-
1478
- if (!baseProps?.logo) {
1479
- log.error("Missing required parameter 'logo'");
1480
- process.exit(1);
1481
- }
1482
-
1483
- const props = getProps({
1484
- assetsOutput: "assets/bootsplash",
1485
- background: "#fff",
1486
- brandWidth: 80,
1487
- logoWidth: 100,
1488
- ...baseProps,
1489
- });
1490
-
1491
- const { executeAddon } = props;
1492
- const addon = executeAddon ? requireAddon() : undefined;
1493
-
1494
- const plugins: Expo.ConfigPlugin<Props>[] = [
1495
- withoutExpoSplashScreen,
1496
- addon?.withGenericAssets ?? withGenericAssets,
1497
- ];
1498
-
1499
- if (platforms.includes("android")) {
1500
- plugins.push(
1501
- addon?.withAndroidAssets ?? withAndroidAssets,
1502
- withAndroidManifest,
1503
- withMainActivity,
1504
- withAndroidStyles,
1505
- withAndroidColors,
1506
- );
1507
-
1508
- if (addon != null) {
1509
- plugins.push(addon.withAndroidColorsNight);
1510
- }
1511
- }
1512
-
1513
- if (platforms.includes("ios")) {
1514
- plugins.push(
1515
- addon?.withIOSAssets ?? withIOSAssets,
1516
- withAppDelegate,
1517
- withInfoPlist,
1518
- withXcodeProject,
1519
- );
1520
- }
1521
-
1522
- if (platforms.includes("web")) {
1523
- plugins.push(addon?.withWebAssets ?? withWebAssets);
1524
- }
1525
-
1526
- return Expo.withPlugins(
1527
- config,
1528
- plugins.map((plugin) => [plugin, props]),
1529
- );
1530
- }, PACKAGE_NAME);