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

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