react-native-bootsplash 6.0.0-beta.0 → 6.0.0-beta.10
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 +33 -88
- package/android/src/main/res/values/public.xml +1 -0
- package/android/src/main/res/values/styles.xml +9 -6
- package/android/src/main/res/values-night/styles.xml +1 -1
- package/android/src/main/res/values-v23/styles.xml +6 -1
- package/android/src/main/res/values-v27/styles.xml +1 -1
- package/android/src/main/res/values-v29/styles.xml +1 -1
- package/android/src/main/res/values-v30/styles.xml +1 -1
- package/app.plugin.js +2 -2
- package/dist/commonjs/addon/index.js +43 -38
- package/dist/commonjs/addon/index.js.map +1 -1
- package/dist/commonjs/expo.js +283 -0
- package/dist/commonjs/expo.js.map +1 -0
- package/dist/commonjs/generate.js +165 -323
- package/dist/commonjs/generate.js.map +1 -1
- package/dist/commonjs/index.js +3 -3
- package/dist/commonjs/index.js.map +1 -1
- package/dist/module/addon/index.js +43 -38
- package/dist/module/addon/index.js.map +1 -1
- package/dist/module/expo.js +273 -0
- package/dist/module/expo.js.map +1 -0
- package/dist/module/generate.js +162 -317
- package/dist/module/generate.js.map +1 -1
- package/dist/module/index.js +2 -2
- package/dist/module/index.js.map +1 -1
- package/dist/typescript/addon/index.d.ts.map +1 -1
- package/dist/typescript/expo.d.ts +11 -0
- package/dist/typescript/expo.d.ts.map +1 -0
- package/dist/typescript/generate.d.ts +27 -21
- package/dist/typescript/generate.d.ts.map +1 -1
- package/dist/typescript/index.d.ts +22 -4
- package/dist/typescript/index.d.ts.map +1 -1
- package/ios/RNBootSplash.mm +33 -37
- package/package.json +8 -8
- package/src/expo.ts +368 -0
- package/src/generate.ts +235 -403
- package/src/index.ts +32 -10
package/src/generate.ts
CHANGED
|
@@ -1,33 +1,32 @@
|
|
|
1
|
-
import murmurhash from "@emotion/hash";
|
|
2
|
-
import { getConfig as getExpoConfig } from "@expo/config";
|
|
3
1
|
import * as Expo from "@expo/config-plugins";
|
|
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
2
|
import plist from "@expo/plist";
|
|
8
3
|
import { findProjectRoot } from "@react-native-community/cli-tools";
|
|
9
4
|
import {
|
|
10
5
|
AndroidProjectConfig,
|
|
11
6
|
IOSProjectConfig,
|
|
12
7
|
} from "@react-native-community/cli-types";
|
|
8
|
+
import childProcess from "child_process";
|
|
9
|
+
import crypto from "crypto";
|
|
13
10
|
import detectIndent from "detect-indent";
|
|
14
11
|
import fs from "fs-extra";
|
|
15
|
-
import { parse as parseHtml } from "node-html-parser";
|
|
12
|
+
import { HTMLElement, parse as parseHtml } from "node-html-parser";
|
|
16
13
|
import path from "path";
|
|
17
14
|
import pc from "picocolors";
|
|
18
15
|
import { Options as PrettierOptions } from "prettier";
|
|
19
16
|
import * as htmlPlugin from "prettier/plugins/html";
|
|
20
17
|
import * as cssPlugin from "prettier/plugins/postcss";
|
|
21
18
|
import * as prettier from "prettier/standalone";
|
|
19
|
+
import semver from "semver";
|
|
22
20
|
import sharp, { Sharp } from "sharp";
|
|
23
21
|
import { dedent } from "ts-dedent";
|
|
22
|
+
import util from "util";
|
|
24
23
|
import formatXml, { XMLFormatterOptions } from "xml-formatter";
|
|
25
24
|
import { Manifest } from ".";
|
|
26
25
|
|
|
27
26
|
const workingPath = process.env.INIT_CWD ?? process.env.PWD ?? process.cwd();
|
|
28
27
|
const projectRoot = findProjectRoot(workingPath);
|
|
29
28
|
|
|
30
|
-
|
|
29
|
+
type Platforms = ("android" | "ios" | "web")[];
|
|
31
30
|
|
|
32
31
|
export type RGBColor = {
|
|
33
32
|
R: string;
|
|
@@ -35,11 +34,16 @@ export type RGBColor = {
|
|
|
35
34
|
B: string;
|
|
36
35
|
};
|
|
37
36
|
|
|
38
|
-
|
|
37
|
+
type Color = {
|
|
39
38
|
hex: string;
|
|
40
39
|
rgb: RGBColor;
|
|
41
40
|
};
|
|
42
41
|
|
|
42
|
+
const promisifiedExec = util.promisify(childProcess.exec);
|
|
43
|
+
|
|
44
|
+
const exec = (cmd: string) =>
|
|
45
|
+
promisifiedExec(cmd).then(({ stdout, stderr }) => stdout || stderr);
|
|
46
|
+
|
|
43
47
|
export const log = {
|
|
44
48
|
error: (text: string) => {
|
|
45
49
|
console.log(pc.red(`❌ ${text}`));
|
|
@@ -60,7 +64,7 @@ export const log = {
|
|
|
60
64
|
},
|
|
61
65
|
};
|
|
62
66
|
|
|
63
|
-
|
|
67
|
+
const parseColor = (value: string): Color => {
|
|
64
68
|
const up = value.toUpperCase().replace(/[^0-9A-F]/g, "");
|
|
65
69
|
|
|
66
70
|
if (up.length !== 3 && up.length !== 6) {
|
|
@@ -180,70 +184,102 @@ export const hfs = {
|
|
|
180
184
|
},
|
|
181
185
|
};
|
|
182
186
|
|
|
183
|
-
export const
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
+
export const getExpoConfig = (projectRoot: string): { isExpo: boolean } => {
|
|
188
|
+
try {
|
|
189
|
+
const pkg = hfs.json(
|
|
190
|
+
path.resolve(projectRoot, "node_modules", "expo", "package.json"),
|
|
191
|
+
) as { version?: string };
|
|
187
192
|
|
|
188
|
-
|
|
189
|
-
const xml = hfs.text(filePath);
|
|
190
|
-
const { indent } = detectIndent(xml);
|
|
193
|
+
const version = pkg.version;
|
|
191
194
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
+
if (version == null || semver.lt(version, "51.0.20")) {
|
|
196
|
+
log.error("Requires Expo 51.0.20 (or higher)");
|
|
197
|
+
process.exit(1);
|
|
198
|
+
}
|
|
195
199
|
|
|
196
|
-
|
|
200
|
+
return { isExpo: true };
|
|
201
|
+
} catch {
|
|
202
|
+
return { isExpo: false };
|
|
203
|
+
}
|
|
197
204
|
};
|
|
198
205
|
|
|
199
|
-
export const
|
|
200
|
-
filePath
|
|
201
|
-
content: string,
|
|
202
|
-
options?: XMLFormatterOptions,
|
|
203
|
-
) => {
|
|
204
|
-
const formatted = formatXml(content, {
|
|
205
|
-
collapseContent: true,
|
|
206
|
-
forceSelfClosingEmptyTag: true,
|
|
207
|
-
indentation: " ",
|
|
208
|
-
lineSeparator: "\n",
|
|
209
|
-
whiteSpaceAtEndOfSelfclosingTag: true,
|
|
210
|
-
...options,
|
|
211
|
-
});
|
|
212
|
-
|
|
213
|
-
hfs.write(filePath, formatted);
|
|
206
|
+
export const writeJson = (filePath: string, content: object) => {
|
|
207
|
+
hfs.write(filePath, JSON.stringify(content, null, 2));
|
|
214
208
|
log.write(filePath);
|
|
215
209
|
};
|
|
216
210
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
211
|
+
type FormatOptions = { indent?: detectIndent.Indent } & (
|
|
212
|
+
| {
|
|
213
|
+
formatter: "prettier";
|
|
214
|
+
selfClosingTags?: boolean;
|
|
215
|
+
useCssPlugin?: boolean;
|
|
216
|
+
htmlWhitespaceSensitivity?: PrettierOptions["htmlWhitespaceSensitivity"];
|
|
217
|
+
singleAttributePerLine?: PrettierOptions["singleAttributePerLine"];
|
|
218
|
+
}
|
|
219
|
+
| {
|
|
220
|
+
formatter: "xmlFormatter";
|
|
221
|
+
whiteSpaceAtEndOfSelfclosingTag?: XMLFormatterOptions["whiteSpaceAtEndOfSelfclosingTag"];
|
|
222
|
+
}
|
|
223
|
+
);
|
|
220
224
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
tabWidth: amount || 2,
|
|
224
|
-
};
|
|
225
|
+
export const readXmlLike = (filePath: string) => {
|
|
226
|
+
const content = hfs.text(filePath);
|
|
225
227
|
|
|
226
|
-
return {
|
|
228
|
+
return {
|
|
229
|
+
root: parseHtml(content),
|
|
230
|
+
formatOptions: { indent: detectIndent(content) },
|
|
231
|
+
};
|
|
227
232
|
};
|
|
228
233
|
|
|
229
|
-
export const
|
|
234
|
+
export const writeXmlLike = async (
|
|
230
235
|
filePath: string,
|
|
231
236
|
content: string,
|
|
232
|
-
|
|
237
|
+
{ indent, ...formatOptions }: FormatOptions,
|
|
233
238
|
) => {
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
239
|
+
if (formatOptions.formatter === "prettier") {
|
|
240
|
+
const {
|
|
241
|
+
formatter,
|
|
242
|
+
useCssPlugin = false,
|
|
243
|
+
selfClosingTags = false,
|
|
244
|
+
...options
|
|
245
|
+
} = formatOptions;
|
|
246
|
+
|
|
247
|
+
const formatted = await prettier.format(content, {
|
|
248
|
+
parser: "html",
|
|
249
|
+
bracketSameLine: true,
|
|
250
|
+
printWidth: 10000,
|
|
251
|
+
plugins: [htmlPlugin, ...(useCssPlugin ? [cssPlugin] : [])],
|
|
252
|
+
useTabs: indent?.type === "tab",
|
|
253
|
+
tabWidth: (indent?.amount ?? 0) || 2,
|
|
254
|
+
...options,
|
|
255
|
+
});
|
|
241
256
|
|
|
242
|
-
|
|
243
|
-
|
|
257
|
+
hfs.write(
|
|
258
|
+
filePath,
|
|
259
|
+
selfClosingTags
|
|
260
|
+
? formatted.replace(/><\/[a-z-0-9]+>/gi, " />")
|
|
261
|
+
: formatted,
|
|
262
|
+
);
|
|
263
|
+
|
|
264
|
+
log.write(filePath);
|
|
265
|
+
} else {
|
|
266
|
+
const { formatter, ...options } = formatOptions;
|
|
267
|
+
|
|
268
|
+
const formatted = formatXml(content, {
|
|
269
|
+
collapseContent: true,
|
|
270
|
+
forceSelfClosingEmptyTag: true,
|
|
271
|
+
lineSeparator: "\n",
|
|
272
|
+
whiteSpaceAtEndOfSelfclosingTag: true,
|
|
273
|
+
indentation: (indent?.indent ?? "") || " ",
|
|
274
|
+
...options,
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
hfs.write(filePath, formatted);
|
|
278
|
+
log.write(filePath);
|
|
279
|
+
}
|
|
244
280
|
};
|
|
245
281
|
|
|
246
|
-
const
|
|
282
|
+
export const cleanIOSAssets = (dir: string) => {
|
|
247
283
|
hfs
|
|
248
284
|
.readDir(dir)
|
|
249
285
|
.filter((file) => file === "Colors.xcassets" || file === "Images.xcassets")
|
|
@@ -316,7 +352,11 @@ const getFileNameSuffix = async ({
|
|
|
316
352
|
.map((key) => record[key])
|
|
317
353
|
.join();
|
|
318
354
|
|
|
319
|
-
return
|
|
355
|
+
return crypto
|
|
356
|
+
.createHash("shake256", { outputLength: 3 })
|
|
357
|
+
.update(stableKey)
|
|
358
|
+
.digest("hex")
|
|
359
|
+
.toLowerCase();
|
|
320
360
|
};
|
|
321
361
|
|
|
322
362
|
const ensureSupportedFormat = async (
|
|
@@ -402,8 +442,8 @@ const getAndroidOutputPath = ({
|
|
|
402
442
|
};
|
|
403
443
|
|
|
404
444
|
const getIOSOutputPath = ({
|
|
405
|
-
assetsOutputPath,
|
|
406
445
|
ios,
|
|
446
|
+
assetsOutputPath,
|
|
407
447
|
isExpo,
|
|
408
448
|
platforms,
|
|
409
449
|
}: {
|
|
@@ -441,10 +481,12 @@ const getIOSOutputPath = ({
|
|
|
441
481
|
return iosOutputPath;
|
|
442
482
|
};
|
|
443
483
|
|
|
444
|
-
const getHtmlTemplatePath = ({
|
|
484
|
+
const getHtmlTemplatePath = async ({
|
|
485
|
+
isExpo,
|
|
445
486
|
html,
|
|
446
487
|
platforms,
|
|
447
488
|
}: {
|
|
489
|
+
isExpo: boolean;
|
|
448
490
|
html: string;
|
|
449
491
|
platforms: Platforms;
|
|
450
492
|
}) => {
|
|
@@ -452,6 +494,24 @@ const getHtmlTemplatePath = ({
|
|
|
452
494
|
return;
|
|
453
495
|
}
|
|
454
496
|
|
|
497
|
+
if (isExpo) {
|
|
498
|
+
const htmlTemplatePath = path.resolve(workingPath, html);
|
|
499
|
+
|
|
500
|
+
const htmlTemplateRelativePath = path.relative(
|
|
501
|
+
workingPath,
|
|
502
|
+
htmlTemplatePath,
|
|
503
|
+
);
|
|
504
|
+
|
|
505
|
+
if (
|
|
506
|
+
htmlTemplateRelativePath === "public/index.html" &&
|
|
507
|
+
!hfs.exists(htmlTemplatePath)
|
|
508
|
+
) {
|
|
509
|
+
const cmd = `npx expo customize ${htmlTemplateRelativePath}`;
|
|
510
|
+
console.log(pc.dim(`Running ${cmd}`));
|
|
511
|
+
await exec(cmd);
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
|
|
455
515
|
const htmlTemplatePath = path.resolve(workingPath, html);
|
|
456
516
|
|
|
457
517
|
if (!hfs.exists(htmlTemplatePath)) {
|
|
@@ -466,7 +526,7 @@ const getHtmlTemplatePath = ({
|
|
|
466
526
|
return htmlTemplatePath;
|
|
467
527
|
};
|
|
468
528
|
|
|
469
|
-
|
|
529
|
+
const getImageHeight = (
|
|
470
530
|
image: Sharp | undefined,
|
|
471
531
|
width: number,
|
|
472
532
|
): Promise<number> => {
|
|
@@ -548,14 +608,9 @@ export const generate = async ({
|
|
|
548
608
|
darkLogo?: string;
|
|
549
609
|
darkBrand?: string;
|
|
550
610
|
}) => {
|
|
551
|
-
const isExpo =
|
|
552
|
-
getExpoConfig(projectRoot, { skipSDKVersionRequirement: true }).exp
|
|
553
|
-
.sdkVersion != null;
|
|
554
|
-
|
|
555
|
-
const [nodeStringVersion = ""] = process.versions.node.split(".");
|
|
556
|
-
const nodeVersion = Number.parseInt(nodeStringVersion, 10);
|
|
611
|
+
const { isExpo } = getExpoConfig(workingPath);
|
|
557
612
|
|
|
558
|
-
if (
|
|
613
|
+
if (semver.lt(process.versions.node, "18.0.0")) {
|
|
559
614
|
log.error("Requires Node 18 (or higher)");
|
|
560
615
|
process.exit(1);
|
|
561
616
|
}
|
|
@@ -670,7 +725,8 @@ export const generate = async ({
|
|
|
670
725
|
platforms,
|
|
671
726
|
});
|
|
672
727
|
|
|
673
|
-
const htmlTemplatePath = getHtmlTemplatePath({
|
|
728
|
+
const htmlTemplatePath = await getHtmlTemplatePath({
|
|
729
|
+
isExpo,
|
|
674
730
|
html,
|
|
675
731
|
platforms,
|
|
676
732
|
});
|
|
@@ -735,6 +791,29 @@ export const generate = async ({
|
|
|
735
791
|
);
|
|
736
792
|
|
|
737
793
|
if (!isExpo) {
|
|
794
|
+
const manifestXmlPath = path.resolve(
|
|
795
|
+
androidOutputPath,
|
|
796
|
+
"..",
|
|
797
|
+
"AndroidManifest.xml",
|
|
798
|
+
);
|
|
799
|
+
|
|
800
|
+
const manifestXml = readXmlLike(manifestXmlPath);
|
|
801
|
+
const activities = manifestXml.root.querySelectorAll("activity");
|
|
802
|
+
|
|
803
|
+
for (const activity of activities) {
|
|
804
|
+
if (activity.getAttribute("android:name") === ".MainActivity") {
|
|
805
|
+
activity.setAttribute("android:theme", "@style/BootTheme");
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
await writeXmlLike(manifestXmlPath, manifestXml.root.toString(), {
|
|
810
|
+
...manifestXml.formatOptions,
|
|
811
|
+
formatter: "prettier",
|
|
812
|
+
htmlWhitespaceSensitivity: "ignore",
|
|
813
|
+
selfClosingTags: true,
|
|
814
|
+
singleAttributePerLine: true,
|
|
815
|
+
});
|
|
816
|
+
|
|
738
817
|
const valuesPath = path.resolve(androidOutputPath, "values");
|
|
739
818
|
hfs.ensureDir(valuesPath);
|
|
740
819
|
|
|
@@ -742,23 +821,86 @@ export const generate = async ({
|
|
|
742
821
|
const colorsXmlEntry = `<color name="bootsplash_background">${background.hex}</color>`;
|
|
743
822
|
|
|
744
823
|
if (hfs.exists(colorsXmlPath)) {
|
|
745
|
-
const
|
|
824
|
+
const colorsXml = readXmlLike(colorsXmlPath);
|
|
746
825
|
const nextColor = parseHtml(colorsXmlEntry);
|
|
747
826
|
|
|
748
|
-
const prevColor = root.querySelector(
|
|
827
|
+
const prevColor = colorsXml.root.querySelector(
|
|
749
828
|
'color[name="bootsplash_background"]',
|
|
750
829
|
);
|
|
751
830
|
|
|
752
831
|
if (prevColor != null) {
|
|
753
832
|
prevColor.replaceWith(nextColor);
|
|
754
833
|
} else {
|
|
755
|
-
root.querySelector("resources")?.appendChild(nextColor);
|
|
834
|
+
colorsXml.root.querySelector("resources")?.appendChild(nextColor);
|
|
756
835
|
}
|
|
757
836
|
|
|
758
|
-
|
|
837
|
+
await writeXmlLike(colorsXmlPath, colorsXml.root.toString(), {
|
|
838
|
+
...colorsXml.formatOptions,
|
|
839
|
+
formatter: "xmlFormatter",
|
|
840
|
+
});
|
|
759
841
|
} else {
|
|
760
|
-
|
|
842
|
+
await writeXmlLike(
|
|
843
|
+
colorsXmlPath,
|
|
844
|
+
`<resources>${colorsXmlEntry}</resources>`,
|
|
845
|
+
{ formatter: "xmlFormatter" },
|
|
846
|
+
);
|
|
761
847
|
}
|
|
848
|
+
|
|
849
|
+
const stylesXmlPath = path.resolve(valuesPath, "styles.xml");
|
|
850
|
+
const stylesXml = readXmlLike(stylesXmlPath);
|
|
851
|
+
|
|
852
|
+
const prevStyle = stylesXml.root.querySelector('style[name="BootTheme"]');
|
|
853
|
+
const parent = prevStyle?.getAttribute("parent") ?? "Theme.BootSplash";
|
|
854
|
+
|
|
855
|
+
const extraItems = parseHtml(
|
|
856
|
+
prevStyle?.text
|
|
857
|
+
.split("\n")
|
|
858
|
+
.map((line) => line.trim())
|
|
859
|
+
.join("") ?? "",
|
|
860
|
+
)
|
|
861
|
+
.childNodes.filter((node) => {
|
|
862
|
+
if (!(node instanceof HTMLElement)) {
|
|
863
|
+
return true;
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
const name = node.getAttribute("name");
|
|
867
|
+
|
|
868
|
+
return (
|
|
869
|
+
name !== "bootSplashBackground" &&
|
|
870
|
+
name !== "bootSplashLogo" &&
|
|
871
|
+
name !== "bootSplashBrand" &&
|
|
872
|
+
name !== "postBootSplashTheme"
|
|
873
|
+
);
|
|
874
|
+
})
|
|
875
|
+
.map((node) => node.toString());
|
|
876
|
+
|
|
877
|
+
const styleItems: string[] = [
|
|
878
|
+
...(extraItems.length > 0 ? [...extraItems, ""] : []),
|
|
879
|
+
|
|
880
|
+
'<item name="bootSplashBackground">@color/bootsplash_background</item>',
|
|
881
|
+
'<item name="bootSplashLogo">@drawable/bootsplash_logo</item>',
|
|
882
|
+
|
|
883
|
+
...(brand != null && brandPath != null
|
|
884
|
+
? ['<item name="bootSplashBrand">@drawable/bootsplash_brand</item>']
|
|
885
|
+
: []),
|
|
886
|
+
|
|
887
|
+
'<item name="postBootSplashTheme">@style/AppTheme</item>',
|
|
888
|
+
];
|
|
889
|
+
|
|
890
|
+
const nextStyle = parseHtml(dedent`
|
|
891
|
+
<style name="BootTheme" parent="${parent}">
|
|
892
|
+
${styleItems.join("\n")}
|
|
893
|
+
</style>
|
|
894
|
+
`);
|
|
895
|
+
|
|
896
|
+
prevStyle?.remove(); // remove the existing style
|
|
897
|
+
stylesXml.root.querySelector("resources")?.appendChild(nextStyle);
|
|
898
|
+
|
|
899
|
+
await writeXmlLike(stylesXmlPath, stylesXml.root.toString(), {
|
|
900
|
+
...stylesXml.formatOptions,
|
|
901
|
+
formatter: "prettier",
|
|
902
|
+
htmlWhitespaceSensitivity: "ignore",
|
|
903
|
+
});
|
|
762
904
|
}
|
|
763
905
|
}
|
|
764
906
|
|
|
@@ -766,7 +908,7 @@ export const generate = async ({
|
|
|
766
908
|
log.title("🍏", "iOS");
|
|
767
909
|
|
|
768
910
|
hfs.ensureDir(iosOutputPath);
|
|
769
|
-
|
|
911
|
+
cleanIOSAssets(iosOutputPath);
|
|
770
912
|
|
|
771
913
|
const storyboardPath = path.resolve(iosOutputPath, "BootSplash.storyboard");
|
|
772
914
|
|
|
@@ -785,7 +927,7 @@ export const generate = async ({
|
|
|
785
927
|
hfs.ensureDir(colorsSetPath);
|
|
786
928
|
hfs.ensureDir(imageSetPath);
|
|
787
929
|
|
|
788
|
-
|
|
930
|
+
await writeXmlLike(
|
|
789
931
|
storyboardPath,
|
|
790
932
|
getStoryboard({
|
|
791
933
|
logoHeight,
|
|
@@ -793,7 +935,10 @@ export const generate = async ({
|
|
|
793
935
|
background: background.rgb,
|
|
794
936
|
fileNameSuffix,
|
|
795
937
|
}),
|
|
796
|
-
{
|
|
938
|
+
{
|
|
939
|
+
formatter: "xmlFormatter",
|
|
940
|
+
whiteSpaceAtEndOfSelfclosingTag: false,
|
|
941
|
+
},
|
|
797
942
|
);
|
|
798
943
|
|
|
799
944
|
writeJson(path.resolve(colorsSetPath, "Contents.json"), {
|
|
@@ -896,18 +1041,19 @@ export const generate = async ({
|
|
|
896
1041
|
|
|
897
1042
|
const project = Expo.IOSConfig.XcodeUtils.getPbxproj(projectRoot);
|
|
898
1043
|
const projectName = path.basename(iosOutputPath);
|
|
1044
|
+
const groupName = path.parse(xcodeProjectPath).name;
|
|
899
1045
|
|
|
900
1046
|
Expo.IOSConfig.XcodeUtils.addResourceFileToGroup({
|
|
901
|
-
filepath: path.join(projectName, "BootSplash.storyboard"),
|
|
902
|
-
groupName: path.parse(xcodeProjectPath).name,
|
|
903
1047
|
project,
|
|
1048
|
+
filepath: path.join(projectName, "BootSplash.storyboard"),
|
|
1049
|
+
groupName,
|
|
904
1050
|
isBuildFile: true,
|
|
905
1051
|
});
|
|
906
1052
|
|
|
907
1053
|
Expo.IOSConfig.XcodeUtils.addResourceFileToGroup({
|
|
908
|
-
filepath: path.join(projectName, "Colors.xcassets"),
|
|
909
|
-
groupName: path.parse(xcodeProjectPath).name,
|
|
910
1054
|
project,
|
|
1055
|
+
filepath: path.join(projectName, "Colors.xcassets"),
|
|
1056
|
+
groupName,
|
|
911
1057
|
isBuildFile: true,
|
|
912
1058
|
});
|
|
913
1059
|
|
|
@@ -919,9 +1065,9 @@ export const generate = async ({
|
|
|
919
1065
|
if (htmlTemplatePath != null) {
|
|
920
1066
|
log.title("🌐", "Web");
|
|
921
1067
|
|
|
922
|
-
const
|
|
1068
|
+
const htmlTemplate = readXmlLike(htmlTemplatePath);
|
|
923
1069
|
const { format } = await logo.metadata();
|
|
924
|
-
const prevStyle = root.querySelector("#bootsplash-style");
|
|
1070
|
+
const prevStyle = htmlTemplate.root.querySelector("#bootsplash-style");
|
|
925
1071
|
|
|
926
1072
|
const base64 = (
|
|
927
1073
|
format === "svg"
|
|
@@ -960,10 +1106,10 @@ export const generate = async ({
|
|
|
960
1106
|
if (prevStyle != null) {
|
|
961
1107
|
prevStyle.replaceWith(nextStyle);
|
|
962
1108
|
} else {
|
|
963
|
-
root.querySelector("head")?.appendChild(nextStyle);
|
|
1109
|
+
htmlTemplate.root.querySelector("head")?.appendChild(nextStyle);
|
|
964
1110
|
}
|
|
965
1111
|
|
|
966
|
-
const prevDiv = root.querySelector("#bootsplash");
|
|
1112
|
+
const prevDiv = htmlTemplate.root.querySelector("#bootsplash");
|
|
967
1113
|
|
|
968
1114
|
const nextDiv = parseHtml(dedent`
|
|
969
1115
|
<div id="bootsplash">
|
|
@@ -974,10 +1120,14 @@ export const generate = async ({
|
|
|
974
1120
|
if (prevDiv != null) {
|
|
975
1121
|
prevDiv.replaceWith(nextDiv);
|
|
976
1122
|
} else {
|
|
977
|
-
root.querySelector("body")?.appendChild(nextDiv);
|
|
1123
|
+
htmlTemplate.root.querySelector("body")?.appendChild(nextDiv);
|
|
978
1124
|
}
|
|
979
1125
|
|
|
980
|
-
await
|
|
1126
|
+
await writeXmlLike(htmlTemplatePath, htmlTemplate.root.toString(), {
|
|
1127
|
+
...htmlTemplate.formatOptions,
|
|
1128
|
+
formatter: "prettier",
|
|
1129
|
+
useCssPlugin: true,
|
|
1130
|
+
});
|
|
981
1131
|
}
|
|
982
1132
|
|
|
983
1133
|
log.title("📄", "Assets");
|
|
@@ -1060,321 +1210,3 @@ ${pc.blue("┗━━━━━━━━━━━━━━━━━━━━━━
|
|
|
1060
1210
|
`\n💖 Thanks for using ${pc.underline("react-native-bootsplash")}`,
|
|
1061
1211
|
);
|
|
1062
1212
|
};
|
|
1063
|
-
|
|
1064
|
-
export type ExpoProps = {
|
|
1065
|
-
assetsDir?: string;
|
|
1066
|
-
edgeToEdge?: boolean;
|
|
1067
|
-
};
|
|
1068
|
-
|
|
1069
|
-
export type ExpoPlugin = Expo.ConfigPlugin<ExpoProps>;
|
|
1070
|
-
|
|
1071
|
-
const withAndroidAssets: ExpoPlugin = (config, props) =>
|
|
1072
|
-
Expo.withDangerousMod(config, [
|
|
1073
|
-
"android",
|
|
1074
|
-
(config) => {
|
|
1075
|
-
const { assetsDir = "assets/bootsplash" } = props;
|
|
1076
|
-
const { platformProjectRoot } = config.modRequest;
|
|
1077
|
-
|
|
1078
|
-
const srcDir = path.resolve(workingPath, assetsDir, "android");
|
|
1079
|
-
|
|
1080
|
-
const destDir = path.resolve(
|
|
1081
|
-
platformProjectRoot,
|
|
1082
|
-
"app",
|
|
1083
|
-
"src",
|
|
1084
|
-
"main",
|
|
1085
|
-
"res",
|
|
1086
|
-
);
|
|
1087
|
-
|
|
1088
|
-
for (const drawableDir of hfs.readDir(srcDir)) {
|
|
1089
|
-
const srcDrawableDir = path.join(srcDir, drawableDir);
|
|
1090
|
-
const destDrawableDir = path.join(destDir, drawableDir);
|
|
1091
|
-
|
|
1092
|
-
hfs.ensureDir(destDrawableDir);
|
|
1093
|
-
|
|
1094
|
-
for (const file of hfs.readDir(srcDrawableDir)) {
|
|
1095
|
-
hfs.copy(
|
|
1096
|
-
path.join(srcDrawableDir, file),
|
|
1097
|
-
path.join(destDrawableDir, file),
|
|
1098
|
-
);
|
|
1099
|
-
}
|
|
1100
|
-
}
|
|
1101
|
-
|
|
1102
|
-
return config;
|
|
1103
|
-
},
|
|
1104
|
-
]);
|
|
1105
|
-
|
|
1106
|
-
const withAndroidManifest: ExpoPlugin = (config) =>
|
|
1107
|
-
Expo.withAndroidManifest(config, (config) => {
|
|
1108
|
-
config.modResults.manifest.application?.forEach((application) => {
|
|
1109
|
-
if (application.$["android:name"] === ".MainApplication") {
|
|
1110
|
-
const { activity } = application;
|
|
1111
|
-
|
|
1112
|
-
activity?.forEach((activity) => {
|
|
1113
|
-
if (activity.$["android:name"] === ".MainActivity") {
|
|
1114
|
-
activity.$["android:theme"] = "@style/BootTheme";
|
|
1115
|
-
}
|
|
1116
|
-
});
|
|
1117
|
-
}
|
|
1118
|
-
});
|
|
1119
|
-
|
|
1120
|
-
return config;
|
|
1121
|
-
});
|
|
1122
|
-
|
|
1123
|
-
const withMainActivity: ExpoPlugin = (config) =>
|
|
1124
|
-
Expo.withMainActivity(config, (config) => {
|
|
1125
|
-
const { modResults } = config;
|
|
1126
|
-
const { language } = modResults;
|
|
1127
|
-
|
|
1128
|
-
const withImports = addImports(
|
|
1129
|
-
modResults.contents.replace(
|
|
1130
|
-
/(\/\/ )?setTheme\(R\.style\.AppTheme\)/,
|
|
1131
|
-
"// setTheme(R.style.AppTheme)",
|
|
1132
|
-
),
|
|
1133
|
-
["android.os.Bundle", "com.zoontek.rnbootsplash.RNBootSplash"],
|
|
1134
|
-
language === "java",
|
|
1135
|
-
);
|
|
1136
|
-
|
|
1137
|
-
// indented with 4 spaces
|
|
1138
|
-
const withInit = mergeContents({
|
|
1139
|
-
src: withImports,
|
|
1140
|
-
comment: " //",
|
|
1141
|
-
tag: "bootsplash-init",
|
|
1142
|
-
offset: 0,
|
|
1143
|
-
anchor: /super\.onCreate\(null\)/,
|
|
1144
|
-
newSrc:
|
|
1145
|
-
" RNBootSplash.init(this, R.style.BootTheme)" +
|
|
1146
|
-
(language === "java" ? ";" : ""),
|
|
1147
|
-
});
|
|
1148
|
-
|
|
1149
|
-
return {
|
|
1150
|
-
...config,
|
|
1151
|
-
modResults: {
|
|
1152
|
-
...modResults,
|
|
1153
|
-
contents: withInit.contents,
|
|
1154
|
-
},
|
|
1155
|
-
};
|
|
1156
|
-
});
|
|
1157
|
-
|
|
1158
|
-
const withAndroidStyles: ExpoPlugin = (config, props) =>
|
|
1159
|
-
Expo.withAndroidStyles(config, async (config) => {
|
|
1160
|
-
const { assetsDir = "assets/bootsplash", edgeToEdge = false } = props;
|
|
1161
|
-
|
|
1162
|
-
const manifest = (await hfs.json(
|
|
1163
|
-
path.resolve(workingPath, assetsDir, "manifest.json"),
|
|
1164
|
-
)) as Manifest;
|
|
1165
|
-
|
|
1166
|
-
const item = [
|
|
1167
|
-
{
|
|
1168
|
-
$: { name: "postBootSplashTheme" },
|
|
1169
|
-
_: "@style/AppTheme",
|
|
1170
|
-
},
|
|
1171
|
-
{
|
|
1172
|
-
$: { name: "bootSplashBackground" },
|
|
1173
|
-
_: "@color/bootsplash_background",
|
|
1174
|
-
},
|
|
1175
|
-
{
|
|
1176
|
-
$: { name: "bootSplashLogo" },
|
|
1177
|
-
_: "@drawable/bootsplash_logo",
|
|
1178
|
-
},
|
|
1179
|
-
];
|
|
1180
|
-
|
|
1181
|
-
if (manifest.brand != null) {
|
|
1182
|
-
item.push({
|
|
1183
|
-
$: { name: "bootSplashBrand" },
|
|
1184
|
-
_: "@drawable/bootsplash_brand",
|
|
1185
|
-
});
|
|
1186
|
-
}
|
|
1187
|
-
|
|
1188
|
-
config.modResults.resources.style
|
|
1189
|
-
?.filter(({ $ }) => $.name !== "BootTheme")
|
|
1190
|
-
.push({
|
|
1191
|
-
item,
|
|
1192
|
-
$: {
|
|
1193
|
-
name: "BootTheme",
|
|
1194
|
-
parent: edgeToEdge
|
|
1195
|
-
? "Theme.BootSplash"
|
|
1196
|
-
: "Theme.BootSplash.EdgeToEdge",
|
|
1197
|
-
},
|
|
1198
|
-
});
|
|
1199
|
-
|
|
1200
|
-
return config;
|
|
1201
|
-
});
|
|
1202
|
-
|
|
1203
|
-
const withAndroidColors: ExpoPlugin = (config, props) =>
|
|
1204
|
-
Expo.withAndroidColors(config, async (config) => {
|
|
1205
|
-
const { assetsDir = "assets/bootsplash" } = props;
|
|
1206
|
-
|
|
1207
|
-
const manifest = (await hfs.json(
|
|
1208
|
-
path.resolve(workingPath, assetsDir, "manifest.json"),
|
|
1209
|
-
)) as Manifest;
|
|
1210
|
-
|
|
1211
|
-
config.modResults = assignColorValue(config.modResults, {
|
|
1212
|
-
name: "bootsplash_background",
|
|
1213
|
-
value: manifest.background,
|
|
1214
|
-
});
|
|
1215
|
-
|
|
1216
|
-
return config;
|
|
1217
|
-
});
|
|
1218
|
-
|
|
1219
|
-
const withAndroidColorsNight: ExpoPlugin = (config, props) =>
|
|
1220
|
-
Expo.withAndroidColorsNight(config, async (config) => {
|
|
1221
|
-
const { assetsDir = "assets/bootsplash" } = props;
|
|
1222
|
-
|
|
1223
|
-
const manifest = (await hfs.json(
|
|
1224
|
-
path.resolve(workingPath, assetsDir, "manifest.json"),
|
|
1225
|
-
)) as Manifest;
|
|
1226
|
-
|
|
1227
|
-
if (manifest.darkBackground != null) {
|
|
1228
|
-
config.modResults = assignColorValue(config.modResults, {
|
|
1229
|
-
name: "bootsplash_background",
|
|
1230
|
-
value: manifest.darkBackground,
|
|
1231
|
-
});
|
|
1232
|
-
}
|
|
1233
|
-
|
|
1234
|
-
return config;
|
|
1235
|
-
});
|
|
1236
|
-
|
|
1237
|
-
const withIOSAssets: ExpoPlugin = (config, props) =>
|
|
1238
|
-
Expo.withDangerousMod(config, [
|
|
1239
|
-
"ios",
|
|
1240
|
-
(config) => {
|
|
1241
|
-
const { assetsDir = "assets/bootsplash" } = props;
|
|
1242
|
-
const { platformProjectRoot, projectName = "" } = config.modRequest;
|
|
1243
|
-
|
|
1244
|
-
const srcDir = path.resolve(workingPath, assetsDir, "ios");
|
|
1245
|
-
const destDir = path.resolve(platformProjectRoot, projectName);
|
|
1246
|
-
|
|
1247
|
-
cleanIOS(destDir);
|
|
1248
|
-
|
|
1249
|
-
hfs.copy(
|
|
1250
|
-
path.join(srcDir, "BootSplash.storyboard"),
|
|
1251
|
-
path.join(destDir, "BootSplash.storyboard"),
|
|
1252
|
-
);
|
|
1253
|
-
|
|
1254
|
-
for (const xcassetsDir of ["Colors.xcassets", "Images.xcassets"]) {
|
|
1255
|
-
const srcXcassetsDir = path.join(srcDir, xcassetsDir);
|
|
1256
|
-
const destXcassetsDir = path.join(destDir, xcassetsDir);
|
|
1257
|
-
|
|
1258
|
-
hfs.ensureDir(destXcassetsDir);
|
|
1259
|
-
|
|
1260
|
-
for (const file of hfs.readDir(srcXcassetsDir)) {
|
|
1261
|
-
hfs.copy(
|
|
1262
|
-
path.join(srcXcassetsDir, file),
|
|
1263
|
-
path.join(destXcassetsDir, file),
|
|
1264
|
-
);
|
|
1265
|
-
}
|
|
1266
|
-
}
|
|
1267
|
-
|
|
1268
|
-
return config;
|
|
1269
|
-
},
|
|
1270
|
-
]);
|
|
1271
|
-
|
|
1272
|
-
const withAppDelegate: ExpoPlugin = (config) =>
|
|
1273
|
-
Expo.withAppDelegate(config, (config) => {
|
|
1274
|
-
const [sdkStringVersion = ""] = config.sdkVersion?.split(".") ?? "";
|
|
1275
|
-
const isAtLeastExpo51 = Number.parseInt(sdkStringVersion, 10) >= 51;
|
|
1276
|
-
|
|
1277
|
-
const { modResults } = config;
|
|
1278
|
-
const { language } = modResults;
|
|
1279
|
-
|
|
1280
|
-
if (language !== "objc" && language !== "objcpp") {
|
|
1281
|
-
throw new Error(
|
|
1282
|
-
`Cannot modify the project AppDelegate as it's not in a supported language: ${language}`,
|
|
1283
|
-
);
|
|
1284
|
-
}
|
|
1285
|
-
|
|
1286
|
-
const withHeader = mergeContents({
|
|
1287
|
-
src: modResults.contents,
|
|
1288
|
-
comment: "//",
|
|
1289
|
-
tag: "bootsplash-header",
|
|
1290
|
-
offset: 1,
|
|
1291
|
-
anchor: /#import "AppDelegate\.h"/,
|
|
1292
|
-
newSrc: '#import "RNBootSplash.h"',
|
|
1293
|
-
});
|
|
1294
|
-
|
|
1295
|
-
const withRootView = mergeContents({
|
|
1296
|
-
src: withHeader.contents,
|
|
1297
|
-
comment: "//",
|
|
1298
|
-
tag: "bootsplash-init",
|
|
1299
|
-
offset: 0,
|
|
1300
|
-
anchor: /@end/,
|
|
1301
|
-
newSrc: isAtLeastExpo51
|
|
1302
|
-
? dedent`
|
|
1303
|
-
- (void)customizeRootView:(RCTRootView *)rootView {
|
|
1304
|
-
[RNBootSplash initWithStoryboard:@"BootSplash" rootView:rootView];
|
|
1305
|
-
}
|
|
1306
|
-
`
|
|
1307
|
-
: dedent`
|
|
1308
|
-
- (UIView *)createRootViewWithBridge:(RCTBridge *)bridge moduleName:(NSString *)moduleName initProps:(NSDictionary *)initProps {
|
|
1309
|
-
UIView *rootView = [super createRootViewWithBridge:bridge moduleName:moduleName initProps:initProps];
|
|
1310
|
-
[RNBootSplash initWithStoryboard:@"BootSplash" rootView:rootView];
|
|
1311
|
-
return rootView;
|
|
1312
|
-
}
|
|
1313
|
-
`,
|
|
1314
|
-
});
|
|
1315
|
-
|
|
1316
|
-
return {
|
|
1317
|
-
...config,
|
|
1318
|
-
modResults: {
|
|
1319
|
-
...modResults,
|
|
1320
|
-
contents: withRootView.contents,
|
|
1321
|
-
},
|
|
1322
|
-
};
|
|
1323
|
-
});
|
|
1324
|
-
|
|
1325
|
-
const withInfoPlist: ExpoPlugin = (config) =>
|
|
1326
|
-
Expo.withInfoPlist(config, (config) => {
|
|
1327
|
-
config.modResults["UILaunchStoryboardName"] = "BootSplash";
|
|
1328
|
-
return config;
|
|
1329
|
-
});
|
|
1330
|
-
|
|
1331
|
-
const withXcodeProject: ExpoPlugin = (config) =>
|
|
1332
|
-
Expo.withXcodeProject(config, (config) => {
|
|
1333
|
-
const { projectName = "" } = config.modRequest;
|
|
1334
|
-
|
|
1335
|
-
Expo.IOSConfig.XcodeUtils.addResourceFileToGroup({
|
|
1336
|
-
filepath: path.join(projectName, "BootSplash.storyboard"),
|
|
1337
|
-
groupName: projectName,
|
|
1338
|
-
project: config.modResults,
|
|
1339
|
-
isBuildFile: true,
|
|
1340
|
-
});
|
|
1341
|
-
|
|
1342
|
-
Expo.IOSConfig.XcodeUtils.addResourceFileToGroup({
|
|
1343
|
-
filepath: path.join(projectName, "Colors.xcassets"),
|
|
1344
|
-
groupName: projectName,
|
|
1345
|
-
project: config.modResults,
|
|
1346
|
-
isBuildFile: true,
|
|
1347
|
-
});
|
|
1348
|
-
|
|
1349
|
-
return config;
|
|
1350
|
-
});
|
|
1351
|
-
|
|
1352
|
-
export const withGenerate: ExpoPlugin = (config, props = {}) => {
|
|
1353
|
-
const plugins: ExpoPlugin[] = [];
|
|
1354
|
-
const { platforms = [] } = config;
|
|
1355
|
-
|
|
1356
|
-
if (platforms.includes("android")) {
|
|
1357
|
-
plugins.push(
|
|
1358
|
-
withAndroidAssets,
|
|
1359
|
-
withAndroidManifest,
|
|
1360
|
-
withMainActivity,
|
|
1361
|
-
withAndroidStyles,
|
|
1362
|
-
withAndroidColors,
|
|
1363
|
-
withAndroidColorsNight,
|
|
1364
|
-
);
|
|
1365
|
-
}
|
|
1366
|
-
|
|
1367
|
-
if (platforms.includes("ios")) {
|
|
1368
|
-
plugins.push(
|
|
1369
|
-
withIOSAssets,
|
|
1370
|
-
withAppDelegate,
|
|
1371
|
-
withInfoPlist,
|
|
1372
|
-
withXcodeProject,
|
|
1373
|
-
);
|
|
1374
|
-
}
|
|
1375
|
-
|
|
1376
|
-
return Expo.withPlugins(
|
|
1377
|
-
config,
|
|
1378
|
-
plugins.map((plugin) => [plugin, props]),
|
|
1379
|
-
);
|
|
1380
|
-
};
|