react-native-bootsplash 5.0.0-beta.3 → 5.0.0-rc.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/README.md +7 -4
  2. package/android/src/main/java/com/zoontek/rnbootsplash/RNBootSplashDialog.java +8 -4
  3. package/android/src/main/java/com/zoontek/rnbootsplash/RNBootSplashModuleImpl.java +1 -0
  4. package/dist/commonjs/NativeRNBootSplash.web.js +43 -0
  5. package/dist/commonjs/NativeRNBootSplash.web.js.map +1 -0
  6. package/dist/commonjs/addon/index.js +82 -1
  7. package/dist/commonjs/addon/index.js.map +1 -1
  8. package/dist/commonjs/generate.js +156 -23
  9. package/dist/commonjs/generate.js.map +1 -1
  10. package/dist/commonjs/index.js +6 -5
  11. package/dist/commonjs/index.js.map +1 -1
  12. package/dist/module/NativeRNBootSplash.web.js +36 -0
  13. package/dist/module/NativeRNBootSplash.web.js.map +1 -0
  14. package/dist/module/addon/index.js +82 -1
  15. package/dist/module/addon/index.js.map +1 -1
  16. package/dist/module/generate.js +153 -22
  17. package/dist/module/generate.js.map +1 -1
  18. package/dist/module/index.js +6 -5
  19. package/dist/module/index.js.map +1 -1
  20. package/dist/typescript/NativeRNBootSplash.web.d.ts +10 -0
  21. package/dist/typescript/NativeRNBootSplash.web.d.ts.map +1 -0
  22. package/dist/typescript/addon/index.d.ts +1 -1
  23. package/dist/typescript/addon/index.d.ts.map +1 -1
  24. package/dist/typescript/generate.d.ts +20 -4
  25. package/dist/typescript/generate.d.ts.map +1 -1
  26. package/dist/typescript/index.d.ts +1 -0
  27. package/dist/typescript/index.d.ts.map +1 -1
  28. package/package.json +5 -2
  29. package/react-native.config.js +15 -10
  30. package/src/NativeRNBootSplash.web.ts +42 -0
  31. package/src/generate.ts +197 -57
  32. package/src/index.ts +4 -1
package/src/generate.ts CHANGED
@@ -5,18 +5,20 @@ import {
5
5
  } from "@react-native-community/cli-types";
6
6
  import detectIndent from "detect-indent";
7
7
  import fs from "fs-extra";
8
+ import { parse as parseHtml } from "node-html-parser";
8
9
  import path from "path";
9
10
  import pc from "picocolors";
10
- import type { Sharp } from "sharp";
11
- import sharp from "sharp";
12
- import xmlFormat, { XMLFormatterOptions } from "xml-formatter";
13
- import type { Manifest } from ".";
11
+ import { Options as PrettierOptions } from "prettier";
12
+ import htmlPlugin from "prettier/parser-html";
13
+ import cssPlugin from "prettier/parser-postcss";
14
+ import prettier from "prettier/standalone";
15
+ import sharp, { Sharp } from "sharp";
16
+ import { dedent } from "ts-dedent";
17
+ import formatXml, { XMLFormatterOptions } from "xml-formatter";
18
+ import { Manifest } from ".";
14
19
 
15
20
  const workingPath = process.env.INIT_CWD ?? process.env.PWD ?? process.cwd();
16
21
 
17
- export const androidColorRegex =
18
- /<color name="bootsplash_background">#\w+<\/color>/g;
19
-
20
22
  export type Color = {
21
23
  hex: string;
22
24
  rgb: { R: string; G: string; B: string };
@@ -54,7 +56,8 @@ const getStoryboard = ({
54
56
  const logoX = (frameWidth - logoWidth) / 2;
55
57
  const logoY = (frameHeight - logoHeight) / 2;
56
58
 
57
- return `<?xml version="1.0" encoding="UTF-8"?>
59
+ return dedent`
60
+ <?xml version="1.0" encoding="UTF-8"?>
58
61
  <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">
59
62
  <device id="retina4_7" orientation="portrait" appearance="light"/>
60
63
  <dependencies>
@@ -117,34 +120,65 @@ export const logWrite = (
117
120
  );
118
121
 
119
122
  export const writeJson = (file: string, json: object) => {
120
- const amount = fs.existsSync(file)
121
- ? detectIndent(fs.readFileSync(file, "utf-8")).amount || 2
122
- : 2;
123
-
124
- fs.writeFileSync(file, JSON.stringify(json, null, amount) + "\n", "utf-8");
123
+ fs.writeFileSync(file, JSON.stringify(json, null, 2) + "\n", "utf-8");
125
124
  logWrite(file);
126
125
  };
127
126
 
127
+ export const readXml = (file: string) => {
128
+ const xml = fs.readFileSync(file, "utf-8");
129
+ const { indent } = detectIndent(xml);
130
+
131
+ const formatOptions: XMLFormatterOptions = {
132
+ indentation: indent || " ",
133
+ };
134
+
135
+ return { root: parseHtml(xml), formatOptions };
136
+ };
137
+
128
138
  export const writeXml = (
129
139
  file: string,
130
140
  xml: string,
131
141
  options?: XMLFormatterOptions,
132
142
  ) => {
133
- const indentation = fs.existsSync(file)
134
- ? detectIndent(fs.readFileSync(file, "utf-8")).indent || " "
135
- : " ";
136
-
137
- fs.writeFileSync(
138
- file,
139
- xmlFormat(xml, {
140
- collapseContent: true,
141
- ...options,
142
- indentation,
143
- lineSeparator: "\n",
144
- }) + "\n",
145
- "utf-8",
146
- );
143
+ const formatted = formatXml(xml, {
144
+ collapseContent: true,
145
+ forceSelfClosingEmptyTag: true,
146
+ indentation: " ",
147
+ lineSeparator: "\n",
148
+ whiteSpaceAtEndOfSelfclosingTag: true,
149
+ ...options,
150
+ });
151
+
152
+ fs.writeFileSync(file, formatted + "\n", "utf-8");
153
+ logWrite(file);
154
+ };
155
+
156
+ export const readHtml = (file: string) => {
157
+ const html = fs.readFileSync(file, "utf-8");
158
+ const { type, amount } = detectIndent(html);
159
+
160
+ const formatOptions: PrettierOptions = {
161
+ useTabs: type === "tab",
162
+ tabWidth: amount || 2,
163
+ };
164
+
165
+ return { root: parseHtml(html), formatOptions };
166
+ };
147
167
 
168
+ export const writeHtml = (
169
+ file: string,
170
+ html: string,
171
+ options?: Omit<PrettierOptions, "parser" | "plugins">,
172
+ ) => {
173
+ const formatted = prettier.format(html, {
174
+ parser: "html",
175
+ plugins: [htmlPlugin, cssPlugin],
176
+ tabWidth: 2,
177
+ useTabs: false,
178
+ ...options,
179
+ });
180
+
181
+ fs.writeFileSync(file, formatted, "utf-8");
148
182
  logWrite(file);
149
183
  };
150
184
 
@@ -234,19 +268,41 @@ const getIOSProjectPath = (ios: IOSProjectConfig): string | undefined => {
234
268
  }
235
269
  };
236
270
 
271
+ const getHtmlTemplatePath = (html: string): string | undefined => {
272
+ const htmlTemplatePath = path.resolve(workingPath, html);
273
+
274
+ if (!fs.existsSync(htmlTemplatePath)) {
275
+ log.warn(
276
+ `No ${path.relative(
277
+ workingPath,
278
+ htmlTemplatePath,
279
+ )} file found. Skipping HTML + CSS generation…`,
280
+ );
281
+ } else {
282
+ return htmlTemplatePath;
283
+ }
284
+ };
285
+
237
286
  export type AddonConfig = {
238
287
  licenseKey: string;
239
288
 
240
289
  androidResPath: string | undefined;
241
290
  iosProjectPath: string | undefined;
291
+ htmlTemplatePath: string | undefined;
242
292
  assetsOutputPath: string | undefined;
243
293
 
294
+ logoPath: string;
295
+ darkLogoPath: string | undefined;
296
+ brandPath: string | undefined;
297
+ darkBrandPath: string | undefined;
298
+
244
299
  logoHeight: number;
245
300
  logoWidth: number;
246
301
  brandHeight: number;
247
302
  brandWidth: number;
248
303
 
249
304
  background: Color;
305
+ logo: Sharp;
250
306
  brand: Sharp | undefined;
251
307
 
252
308
  darkBackground: Color | undefined;
@@ -266,11 +322,12 @@ const requireAddon = ():
266
322
  };
267
323
 
268
324
  export const generate: CommandFunction<{
269
- logoWidth: number;
325
+ platforms: string[];
270
326
  background: string;
327
+ logoWidth: number;
271
328
  assetsOutput?: string;
329
+ html: string;
272
330
  flavor: string;
273
- platforms: string[];
274
331
 
275
332
  licenseKey?: string;
276
333
  brand?: string;
@@ -281,7 +338,7 @@ export const generate: CommandFunction<{
281
338
  }> = async (
282
339
  [argsLogo],
283
340
  { project: { android, ios } },
284
- { flavor, platforms, licenseKey, ...args },
341
+ { platforms, html, flavor, licenseKey, ...args },
285
342
  ) => {
286
343
  const [nodeStringVersion = ""] = process.versions.node.split(".");
287
344
  const nodeVersion = parseInt(nodeStringVersion, 10);
@@ -296,34 +353,38 @@ export const generate: CommandFunction<{
296
353
  process.exit(1);
297
354
  }
298
355
 
356
+ const logoPath = path.resolve(workingPath, argsLogo);
357
+
358
+ const darkLogoPath =
359
+ args.darkLogo != null
360
+ ? path.resolve(workingPath, args.darkLogo)
361
+ : undefined;
362
+
363
+ const brandPath =
364
+ args.brand != null ? path.resolve(workingPath, args.brand) : undefined;
365
+
366
+ const darkBrandPath =
367
+ args.darkBrand != null
368
+ ? path.resolve(workingPath, args.darkBrand)
369
+ : undefined;
370
+
299
371
  const assetsOutputPath =
300
372
  args.assetsOutput != null
301
373
  ? path.resolve(workingPath, args.assetsOutput)
302
374
  : undefined;
303
375
 
376
+ const logo = sharp(logoPath);
377
+ const darkLogo = darkLogoPath != null ? sharp(darkLogoPath) : undefined;
378
+ const brand = brandPath != null ? sharp(brandPath) : undefined;
379
+ const darkBrand = darkBrandPath != null ? sharp(darkBrandPath) : undefined;
380
+
304
381
  const background = parseColor(args.background);
305
- const logo = sharp(path.resolve(workingPath, argsLogo));
306
382
  const logoWidth = args.logoWidth - (args.logoWidth % 2);
307
383
  const brandWidth = args.brandWidth - (args.brandWidth % 2);
308
384
 
309
- const brand =
310
- args.brand != null
311
- ? sharp(path.resolve(workingPath, args.brand))
312
- : undefined;
313
-
314
385
  const darkBackground =
315
386
  args.darkBackground != null ? parseColor(args.darkBackground) : undefined;
316
387
 
317
- const darkLogo =
318
- args.darkLogo != null
319
- ? sharp(path.resolve(workingPath, args.darkLogo))
320
- : undefined;
321
-
322
- const darkBrand =
323
- args.darkBrand != null
324
- ? sharp(path.resolve(workingPath, args.darkBrand))
325
- : undefined;
326
-
327
388
  const executeAddon =
328
389
  brand != null ||
329
390
  darkBackground != null ||
@@ -403,6 +464,10 @@ export const generate: CommandFunction<{
403
464
  ? getIOSProjectPath(ios)
404
465
  : undefined;
405
466
 
467
+ const htmlTemplatePath = platforms.includes("web")
468
+ ? getHtmlTemplatePath(html)
469
+ : undefined;
470
+
406
471
  if (androidResPath != null) {
407
472
  log.title("🤖", "Android");
408
473
 
@@ -413,13 +478,19 @@ export const generate: CommandFunction<{
413
478
  const colorsXmlEntry = `<color name="bootsplash_background">${background.hex}</color>`;
414
479
 
415
480
  if (fs.existsSync(colorsXmlPath)) {
416
- const colorsXml = fs.readFileSync(colorsXmlPath, "utf-8");
417
-
418
- const nextXml = colorsXml.match(androidColorRegex)
419
- ? colorsXml.replace(androidColorRegex, colorsXmlEntry)
420
- : colorsXml.replace(/<\/resources>/g, `${colorsXmlEntry}</resources>`);
421
-
422
- writeXml(colorsXmlPath, nextXml);
481
+ const { root, formatOptions } = readXml(colorsXmlPath);
482
+ const nextColor = parseHtml(colorsXmlEntry);
483
+ const prevColor = root.querySelector(
484
+ 'color[name="bootsplash_background"]',
485
+ );
486
+
487
+ if (prevColor != null) {
488
+ prevColor.replaceWith(nextColor);
489
+ } else {
490
+ root.querySelector("resources")?.appendChild(nextColor);
491
+ }
492
+
493
+ writeXml(colorsXmlPath, root.toString(), formatOptions);
423
494
  } else {
424
495
  writeXml(colorsXmlPath, `<resources>${colorsXmlEntry}</resources>`);
425
496
  }
@@ -487,18 +558,16 @@ export const generate: CommandFunction<{
487
558
  "BootSplash.storyboard",
488
559
  );
489
560
 
490
- fs.writeFileSync(
561
+ writeXml(
491
562
  storyboardPath,
492
563
  getStoryboard({
493
564
  logoHeight,
494
565
  logoWidth,
495
566
  background: background.rgb,
496
567
  }),
497
- "utf-8",
568
+ { whiteSpaceAtEndOfSelfclosingTag: false },
498
569
  );
499
570
 
500
- logWrite(storyboardPath);
501
-
502
571
  const imageSetPath = path.resolve(
503
572
  iosProjectPath,
504
573
  "Images.xcassets",
@@ -554,6 +623,70 @@ export const generate: CommandFunction<{
554
623
  );
555
624
  }
556
625
 
626
+ if (htmlTemplatePath != null) {
627
+ log.title("🌐", "Web");
628
+
629
+ const { root, formatOptions } = readHtml(htmlTemplatePath);
630
+ const { format } = await logo.metadata();
631
+ const prevStyle = root.querySelector("#bootsplash-style");
632
+
633
+ const base64 = (
634
+ format === "svg"
635
+ ? fs.readFileSync(logoPath)
636
+ : await logo
637
+ .clone()
638
+ .resize(Math.round(logoWidth * 2))
639
+ .png({ quality: 100 })
640
+ .toBuffer()
641
+ ).toString("base64");
642
+
643
+ const dataURI = `data:image/${format ? "svg+xml" : "png"};base64,${base64}`;
644
+
645
+ const nextStyle = parseHtml(dedent`
646
+ <style id="bootsplash-style">
647
+ #bootsplash {
648
+ position: absolute;
649
+ top: 0;
650
+ bottom: 0;
651
+ left: 0;
652
+ right: 0;
653
+ overflow: hidden;
654
+ display: flex;
655
+ justify-content: center;
656
+ align-items: center;
657
+ background-color: ${background.hex};
658
+ }
659
+ #bootsplash-logo {
660
+ content: url("${dataURI}");
661
+ width: ${logoWidth}px;
662
+ height: ${logoHeight}px;
663
+ }
664
+ </style>
665
+ `);
666
+
667
+ if (prevStyle != null) {
668
+ prevStyle.replaceWith(nextStyle);
669
+ } else {
670
+ root.querySelector("head")?.appendChild(nextStyle);
671
+ }
672
+
673
+ const prevDiv = root.querySelector("#bootsplash");
674
+
675
+ const nextDiv = parseHtml(dedent`
676
+ <div id="bootsplash">
677
+ <div id="bootsplash-logo"></div>
678
+ </div>
679
+ `);
680
+
681
+ if (prevDiv != null) {
682
+ prevDiv.replaceWith(nextDiv);
683
+ } else {
684
+ root.querySelector("body")?.appendChild(nextDiv);
685
+ }
686
+
687
+ writeHtml(htmlTemplatePath, root.toString(), formatOptions);
688
+ }
689
+
557
690
  if (assetsOutputPath != null) {
558
691
  log.title("📄", "Assets");
559
692
 
@@ -600,6 +733,7 @@ export const generate: CommandFunction<{
600
733
 
601
734
  androidResPath,
602
735
  iosProjectPath,
736
+ htmlTemplatePath,
603
737
  assetsOutputPath,
604
738
 
605
739
  logoHeight,
@@ -607,7 +741,13 @@ export const generate: CommandFunction<{
607
741
  brandHeight,
608
742
  brandWidth,
609
743
 
744
+ logoPath,
745
+ darkLogoPath,
746
+ brandPath,
747
+ darkBrandPath,
748
+
610
749
  background,
750
+ logo,
611
751
  brand,
612
752
 
613
753
  darkBackground,
package/src/index.ts CHANGED
@@ -22,6 +22,7 @@ export type Manifest = {
22
22
  height: number;
23
23
  };
24
24
  brand?: {
25
+ bottom: number;
25
26
  width: number;
26
27
  height: number;
27
28
  };
@@ -74,6 +75,7 @@ export function useHideAnimation(config: UseHideAnimationConfig) {
74
75
  const hasBrand = manifest.brand != null && brandSrc != null;
75
76
  const logoWidth = manifest.logo.width;
76
77
  const logoHeight = manifest.logo.height;
78
+ const brandBottom = manifest.brand?.bottom;
77
79
  const brandWidth = manifest.brand?.width;
78
80
  const brandHeight = manifest.brand?.height;
79
81
 
@@ -156,7 +158,7 @@ export function useHideAnimation(config: UseHideAnimationConfig) {
156
158
  source: brandFinalSrc,
157
159
  style: {
158
160
  position: "absolute",
159
- bottom: 60,
161
+ bottom: Platform.OS === "web" ? 60 : brandBottom,
160
162
  width: brandWidth,
161
163
  height: brandHeight,
162
164
  },
@@ -193,6 +195,7 @@ export function useHideAnimation(config: UseHideAnimationConfig) {
193
195
 
194
196
  logoWidth,
195
197
  logoHeight,
198
+ brandBottom,
196
199
  brandWidth,
197
200
  brandHeight,
198
201