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