vxrn 2.0.0-beta.131.1 → 2.0.0-beta.132.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/dist/exports/prebuildWithoutExpo.mjs +206 -43
- package/dist/exports/prebuildWithoutExpo.mjs.map +1 -1
- package/dist/exports/prebuildWithoutExpo.native.js +311 -52
- package/dist/exports/prebuildWithoutExpo.native.js.map +1 -1
- package/package.json +10 -10
- package/src/exports/prebuildWithoutExpo.test.ts +149 -0
- package/src/exports/prebuildWithoutExpo.ts +269 -45
- package/types/exports/prebuildWithoutExpo.d.ts.map +1 -1
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { execFileSync } from "node:child_process";
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
2
3
|
import module from "node:module";
|
|
3
4
|
import path from "node:path";
|
|
4
5
|
import { pathToFileURL } from "node:url";
|
|
@@ -247,6 +248,54 @@ function patchIosPbxprojSceneDelegate(rendered, appName) {
|
|
|
247
248
|
}
|
|
248
249
|
return patched;
|
|
249
250
|
}
|
|
251
|
+
function fontFileName(font) {
|
|
252
|
+
return path.basename(font);
|
|
253
|
+
}
|
|
254
|
+
function pbxprojId(seed) {
|
|
255
|
+
return createHash("sha1").update(seed).digest("hex").slice(0, 24).toUpperCase();
|
|
256
|
+
}
|
|
257
|
+
const GOOGLE_SERVICES_PLIST = "GoogleService-Info.plist";
|
|
258
|
+
const GOOGLE_SERVICES_VERSION = "4.4.4";
|
|
259
|
+
function iosResourceFiles(app) {
|
|
260
|
+
return [...(app.fonts ?? []).map(fontFileName), ...app.ios?.googleServicesFile ? [GOOGLE_SERVICES_PLIST] : []];
|
|
261
|
+
}
|
|
262
|
+
function patchIosPbxprojResources(rendered, appName, names) {
|
|
263
|
+
let patched = rendered;
|
|
264
|
+
for (const name of names) {
|
|
265
|
+
const fileRef = pbxprojId(`one-resource-file:${name}`);
|
|
266
|
+
const buildFile = pbxprojId(`one-resource-build:${name}`);
|
|
267
|
+
const fileType = name.endsWith(".plist") ? "text.plist.xml" : "file";
|
|
268
|
+
const edits = [
|
|
269
|
+
["/* Images.xcassets in Resources */ = {isa = PBXBuildFile;", ` ${buildFile} /* ${name} in Resources */ = {isa = PBXBuildFile; fileRef = ${fileRef} /* ${name} */; };`],
|
|
270
|
+
["/* Images.xcassets */ = {isa = PBXFileReference;", ` ${fileRef} /* ${name} */ = {isa = PBXFileReference; lastKnownFileType = ${fileType}; name = ${name}; path = ${appName}/${name}; sourceTree = "<group>"; };`],
|
|
271
|
+
["/* Images.xcassets */,", ` ${fileRef} /* ${name} */,`],
|
|
272
|
+
["/* Images.xcassets in Resources */,", ` ${buildFile} /* ${name} in Resources */,`]
|
|
273
|
+
];
|
|
274
|
+
for (const [anchor, insertion] of edits) {
|
|
275
|
+
const next = insertAfterLine(patched, anchor, insertion);
|
|
276
|
+
if (next === patched) {
|
|
277
|
+
throw new Error(`[vxrn] prebuild template project.pbxproj lost its Images.xcassets anchor (${anchor})`);
|
|
278
|
+
}
|
|
279
|
+
patched = next;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
return patched;
|
|
283
|
+
}
|
|
284
|
+
function copyAppResources({ root, dest, platform, app }) {
|
|
285
|
+
const copies = (app.fonts ?? []).map((font) => [font, platform === "ios" ? path.join(dest, app.name, fontFileName(font)) : path.join(dest, "app", "src", "main", "assets", "fonts", fontFileName(font))]);
|
|
286
|
+
const googleServices = platform === "ios" ? app.ios?.googleServicesFile : app.android?.googleServicesFile;
|
|
287
|
+
if (googleServices) {
|
|
288
|
+
copies.push([googleServices, platform === "ios" ? path.join(dest, app.name, GOOGLE_SERVICES_PLIST) : path.join(dest, "app", "google-services.json")]);
|
|
289
|
+
}
|
|
290
|
+
for (const [file, target] of copies) {
|
|
291
|
+
const source = path.resolve(root, file);
|
|
292
|
+
if (!FSExtra.existsSync(source)) {
|
|
293
|
+
throw new Error(`[vxrn] native.app names ${file}, which does not exist`);
|
|
294
|
+
}
|
|
295
|
+
FSExtra.mkdirSync(path.dirname(target), { recursive: true });
|
|
296
|
+
FSExtra.copyFileSync(source, target);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
250
299
|
function patchIosPbxprojAppEntitlements(rendered, appName) {
|
|
251
300
|
const edits = [["/* AppDelegate.swift */ = {isa = PBXFileReference;", ` ${PUSH_ENTITLEMENTS_FILE_REF_ID} /* ${appName}.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; name = ${appName}.entitlements; path = ${appName}/${appName}.entitlements; sourceTree = "<group>"; };`], ["/* AppDelegate.swift */,", ` ${PUSH_ENTITLEMENTS_FILE_REF_ID} /* ${appName}.entitlements */,`]];
|
|
252
301
|
let patched = rendered;
|
|
@@ -1052,38 +1101,85 @@ ${adaptiveIcon.monochrome ? " <monochrome android:drawable=\"@mipmap/ic_launc
|
|
|
1052
1101
|
async function generateSplashScreen(args) {
|
|
1053
1102
|
const { root, dest, platform, app } = args;
|
|
1054
1103
|
if (!app.splash) return;
|
|
1055
|
-
const source = path.resolve(root, app.splash.source);
|
|
1056
|
-
if (!FSExtra.existsSync(source)) {
|
|
1057
|
-
throw new Error(`[vxrn] native.app.splash source does not exist: ${source}`);
|
|
1058
|
-
}
|
|
1059
1104
|
const cover = app.splash.resizeMode === "cover";
|
|
1060
|
-
const
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1105
|
+
const prepareArtwork = async (sourcePath, backgroundColor) => {
|
|
1106
|
+
const source = path.resolve(root, sourcePath);
|
|
1107
|
+
if (!FSExtra.existsSync(source)) {
|
|
1108
|
+
throw new Error(`[vxrn] native.app.splash source does not exist: ${source}`);
|
|
1109
|
+
}
|
|
1110
|
+
const image = sharp(source).rotate();
|
|
1111
|
+
const prepared = await (cover ? image : image.trim({ background: backgroundColor })).png().toBuffer({ resolveWithObject: true });
|
|
1112
|
+
if (!prepared.info.width || !prepared.info.height) {
|
|
1113
|
+
throw new Error("[vxrn] native.app.splash source must be an image");
|
|
1114
|
+
}
|
|
1115
|
+
return prepared;
|
|
1116
|
+
};
|
|
1117
|
+
const { data: artwork, info: metadata } = await prepareArtwork(app.splash.source, app.splash.backgroundColor);
|
|
1118
|
+
const dark = app.splash.dark;
|
|
1119
|
+
const darkArtwork = dark?.source ? (await prepareArtwork(dark.source, dark.backgroundColor)).data : void 0;
|
|
1065
1120
|
const artworkWidth = app.splash.width ?? 200;
|
|
1066
1121
|
const artworkHeight = Number((artworkWidth * (metadata.height / metadata.width)).toFixed(3));
|
|
1067
1122
|
if (platform === "ios") {
|
|
1068
1123
|
const appDir = path.join(dest, app.name);
|
|
1069
1124
|
const splashDir = path.join(appDir, "Images.xcassets", "Splash.imageset");
|
|
1070
1125
|
FSExtra.mkdirSync(splashDir, { recursive: true });
|
|
1126
|
+
const darkAppearance = [{
|
|
1127
|
+
appearance: "luminosity",
|
|
1128
|
+
value: "dark"
|
|
1129
|
+
}];
|
|
1071
1130
|
await sharp(artwork).toFile(path.join(splashDir, "splash.png"));
|
|
1131
|
+
if (darkArtwork) await sharp(darkArtwork).toFile(path.join(splashDir, "splash-dark.png"));
|
|
1072
1132
|
FSExtra.writeFileSync(path.join(splashDir, "Contents.json"), `${JSON.stringify({
|
|
1073
1133
|
images: [{
|
|
1074
1134
|
filename: "splash.png",
|
|
1075
1135
|
idiom: "universal",
|
|
1076
1136
|
scale: "1x"
|
|
1077
|
-
}
|
|
1137
|
+
}, ...darkArtwork ? [{
|
|
1138
|
+
appearances: darkAppearance,
|
|
1139
|
+
filename: "splash-dark.png",
|
|
1140
|
+
idiom: "universal",
|
|
1141
|
+
scale: "1x"
|
|
1142
|
+
}] : []],
|
|
1078
1143
|
info: {
|
|
1079
1144
|
author: "xcode",
|
|
1080
1145
|
version: 1
|
|
1081
1146
|
}
|
|
1082
1147
|
}, null, 2)}
|
|
1083
1148
|
`);
|
|
1084
|
-
const
|
|
1085
|
-
|
|
1086
|
-
|
|
1149
|
+
const srgb = (hex) => ({
|
|
1150
|
+
red: Number.parseInt(hex.slice(1, 3), 16) / 255,
|
|
1151
|
+
green: Number.parseInt(hex.slice(3, 5), 16) / 255,
|
|
1152
|
+
blue: Number.parseInt(hex.slice(5, 7), 16) / 255
|
|
1153
|
+
});
|
|
1154
|
+
const colorEntry = (hex) => {
|
|
1155
|
+
const { red: red2, green: green2, blue: blue2 } = srgb(hex);
|
|
1156
|
+
return {
|
|
1157
|
+
color: {
|
|
1158
|
+
"color-space": "srgb",
|
|
1159
|
+
components: {
|
|
1160
|
+
red: red2.toFixed(3),
|
|
1161
|
+
green: green2.toFixed(3),
|
|
1162
|
+
blue: blue2.toFixed(3),
|
|
1163
|
+
alpha: "1.000"
|
|
1164
|
+
}
|
|
1165
|
+
},
|
|
1166
|
+
idiom: "universal"
|
|
1167
|
+
};
|
|
1168
|
+
};
|
|
1169
|
+
const colorDir = path.join(appDir, "Images.xcassets", "SplashBackground.colorset");
|
|
1170
|
+
FSExtra.mkdirSync(colorDir, { recursive: true });
|
|
1171
|
+
FSExtra.writeFileSync(path.join(colorDir, "Contents.json"), `${JSON.stringify({
|
|
1172
|
+
colors: [colorEntry(app.splash.backgroundColor), ...dark ? [{
|
|
1173
|
+
appearances: darkAppearance,
|
|
1174
|
+
...colorEntry(dark.backgroundColor)
|
|
1175
|
+
}] : []],
|
|
1176
|
+
info: {
|
|
1177
|
+
author: "xcode",
|
|
1178
|
+
version: 1
|
|
1179
|
+
}
|
|
1180
|
+
}, null, 2)}
|
|
1181
|
+
`);
|
|
1182
|
+
const { red, green, blue } = srgb(app.splash.backgroundColor);
|
|
1087
1183
|
FSExtra.writeFileSync(path.join(appDir, "LaunchScreen.storyboard"), `<?xml version="1.0" encoding="UTF-8"?>
|
|
1088
1184
|
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="15702" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="launch-controller">
|
|
1089
1185
|
<device id="retina6_12" orientation="portrait" appearance="light"/>
|
|
@@ -1101,7 +1197,7 @@ async function generateSplashScreen(args) {
|
|
|
1101
1197
|
<subviews>
|
|
1102
1198
|
<imageView userInteractionEnabled="NO" contentMode="${cover ? "scaleAspectFill" : "scaleAspectFit"}" image="Splash" translatesAutoresizingMaskIntoConstraints="NO" id="splash-image"/>
|
|
1103
1199
|
</subviews>
|
|
1104
|
-
<color key="backgroundColor"
|
|
1200
|
+
<color key="backgroundColor" name="SplashBackground"/>
|
|
1105
1201
|
<constraints>
|
|
1106
1202
|
${cover ? ` <constraint firstItem="splash-image" firstAttribute="leading" secondItem="launch-view" secondAttribute="leading" id="splash-leading"/>
|
|
1107
1203
|
<constraint firstItem="splash-image" firstAttribute="trailing" secondItem="launch-view" secondAttribute="trailing" id="splash-trailing"/>
|
|
@@ -1119,6 +1215,9 @@ ${cover ? ` <constraint firstItem="splash-image" firstAttribute="le
|
|
|
1119
1215
|
</scenes>
|
|
1120
1216
|
<resources>
|
|
1121
1217
|
<image name="Splash" width="${metadata.width}" height="${metadata.height}"/>
|
|
1218
|
+
<namedColor name="SplashBackground">
|
|
1219
|
+
<color red="${red}" green="${green}" blue="${blue}" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
|
1220
|
+
</namedColor>
|
|
1122
1221
|
</resources>
|
|
1123
1222
|
</document>
|
|
1124
1223
|
`);
|
|
@@ -1126,35 +1225,39 @@ ${cover ? ` <constraint firstItem="splash-image" firstAttribute="le
|
|
|
1126
1225
|
}
|
|
1127
1226
|
const mainRes = path.join(dest, "app", "src", "main", "res");
|
|
1128
1227
|
const drawable = path.join(mainRes, "drawable");
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
const
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1228
|
+
const variants = [[artwork, "drawable"]];
|
|
1229
|
+
if (darkArtwork) variants.push([darkArtwork, "drawable-night"]);
|
|
1230
|
+
for (const [variantArtwork, drawablePrefix] of variants) {
|
|
1231
|
+
for (const [density, multiplier] of Object.entries(ANDROID_DENSITIES)) {
|
|
1232
|
+
const canvasSize = 288 * multiplier;
|
|
1233
|
+
const imageSize = Math.round(artworkWidth * multiplier);
|
|
1234
|
+
const contained = await sharp(variantArtwork).resize(imageSize, imageSize, {
|
|
1235
|
+
fit: "contain",
|
|
1236
|
+
background: {
|
|
1237
|
+
r: 0,
|
|
1238
|
+
g: 0,
|
|
1239
|
+
b: 0,
|
|
1240
|
+
alpha: 0
|
|
1241
|
+
}
|
|
1242
|
+
}).png().toBuffer();
|
|
1243
|
+
const drawableDensity = path.join(mainRes, `${drawablePrefix}-${density}`);
|
|
1244
|
+
FSExtra.mkdirSync(drawableDensity, { recursive: true });
|
|
1245
|
+
await sharp({ create: {
|
|
1246
|
+
width: canvasSize,
|
|
1247
|
+
height: canvasSize,
|
|
1248
|
+
channels: 4,
|
|
1249
|
+
background: {
|
|
1250
|
+
r: 0,
|
|
1251
|
+
g: 0,
|
|
1252
|
+
b: 0,
|
|
1253
|
+
alpha: 0
|
|
1254
|
+
}
|
|
1255
|
+
} }).composite([{
|
|
1256
|
+
input: contained,
|
|
1257
|
+
left: Math.round((canvasSize - imageSize) / 2),
|
|
1258
|
+
top: Math.round((canvasSize - imageSize) / 2)
|
|
1259
|
+
}]).png().toFile(path.join(drawableDensity, "splash.png"));
|
|
1260
|
+
}
|
|
1158
1261
|
}
|
|
1159
1262
|
FSExtra.writeFileSync(path.join(drawable, "launch_screen.xml"), `<?xml version="1.0" encoding="utf-8"?>
|
|
1160
1263
|
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
|
@@ -1169,6 +1272,14 @@ ${cover ? ` <constraint firstItem="splash-image" firstAttribute="le
|
|
|
1169
1272
|
<color name="splash_background">${app.splash.backgroundColor}</color>
|
|
1170
1273
|
</resources>
|
|
1171
1274
|
`);
|
|
1275
|
+
if (dark) {
|
|
1276
|
+
FSExtra.mkdirSync(path.join(mainRes, "values-night"), { recursive: true });
|
|
1277
|
+
FSExtra.writeFileSync(path.join(mainRes, "values-night", "colors.xml"), `<?xml version="1.0" encoding="utf-8"?>
|
|
1278
|
+
<resources>
|
|
1279
|
+
<color name="splash_background">${dark.backgroundColor}</color>
|
|
1280
|
+
</resources>
|
|
1281
|
+
`);
|
|
1282
|
+
}
|
|
1172
1283
|
const stylesPath = path.join(mainRes, "values", "styles.xml");
|
|
1173
1284
|
const styles = FSExtra.readFileSync(stylesPath, "utf8").replace(" <!-- Customize your theme here. -->", " <item name=\"android:windowBackground\">@drawable/launch_screen</item>");
|
|
1174
1285
|
FSExtra.writeFileSync(stylesPath, styles);
|
|
@@ -1250,6 +1361,12 @@ ${schemes.map((scheme) => ` <string>${scheme}</string>`).join("\n")}
|
|
|
1250
1361
|
}
|
|
1251
1362
|
stamps.push(` <key>${updatesHost.runtimeVersionInfoPlistKey}</key>
|
|
1252
1363
|
<string>${escapeXml(app.updates.runtimeVersion)}</string>`);
|
|
1364
|
+
}
|
|
1365
|
+
if (app.fonts?.length) {
|
|
1366
|
+
stamps.push(` <key>UIAppFonts</key>
|
|
1367
|
+
<array>
|
|
1368
|
+
${app.fonts.map((font) => ` <string>${escapeXml(fontFileName(font))}</string>`).join("\n")}
|
|
1369
|
+
</array>`);
|
|
1253
1370
|
}
|
|
1254
1371
|
if (app.userInterfaceStyle !== void 0) {
|
|
1255
1372
|
stamps.push(` <key>UIUserInterfaceStyle</key>
|
|
@@ -1523,6 +1640,9 @@ end`);
|
|
|
1523
1640
|
rendered = patchIosPbxprojAppEntitlements(rendered, appName);
|
|
1524
1641
|
}
|
|
1525
1642
|
if (app.ios?.widgets) rendered = patchIosPbxprojWidgets(rendered, app);
|
|
1643
|
+
if (iosResourceFiles(app).length) {
|
|
1644
|
+
rendered = patchIosPbxprojResources(rendered, appName, iosResourceFiles(app));
|
|
1645
|
+
}
|
|
1526
1646
|
}
|
|
1527
1647
|
if (platform === "ios" && relativePath === "Podfile") {
|
|
1528
1648
|
if (app.ios?.ccache) rendered = `ENV['USE_CCACHE'] ||= '1'
|
|
@@ -1565,6 +1685,43 @@ use_frameworks! :linkage => :${app.ios.useFrameworks}`);
|
|
|
1565
1685
|
}
|
|
1566
1686
|
rendered = rendered.replace(hardcodedGradlePlugin, `includeBuild(new File(["node", "--print", "require('module').createRequire(require.resolve('react-native/package.json')).resolve('@react-native/gradle-plugin/package.json')"].execute(null, settingsDir).text.trim()).parentFile.canonicalPath)`);
|
|
1567
1687
|
}
|
|
1688
|
+
if (platform === "android" && relativePath === "app/build.gradle" && app.android?.minify) {
|
|
1689
|
+
const minify = "def enableProguardInReleaseBuilds = false";
|
|
1690
|
+
const minifyEnabled = "minifyEnabled enableProguardInReleaseBuilds";
|
|
1691
|
+
if (!rendered.includes(minify) || !rendered.includes(minifyEnabled)) {
|
|
1692
|
+
throw new Error("[vxrn] prebuild template app/build.gradle lost its proguard switch");
|
|
1693
|
+
}
|
|
1694
|
+
rendered = rendered.replace(minify, "def enableProguardInReleaseBuilds = true");
|
|
1695
|
+
if (app.android.shrinkResources) {
|
|
1696
|
+
rendered = rendered.replace(minifyEnabled, `${minifyEnabled}
|
|
1697
|
+
shrinkResources true`);
|
|
1698
|
+
}
|
|
1699
|
+
}
|
|
1700
|
+
if (platform === "android" && app.android?.googleServicesFile) {
|
|
1701
|
+
const edits = [[
|
|
1702
|
+
"build.gradle",
|
|
1703
|
+
"classpath(\"org.jetbrains.kotlin:kotlin-gradle-plugin\")",
|
|
1704
|
+
` classpath("com.google.gms:google-services:${GOOGLE_SERVICES_VERSION}")`
|
|
1705
|
+
], [
|
|
1706
|
+
"app/build.gradle",
|
|
1707
|
+
"apply plugin: \"com.facebook.react\"",
|
|
1708
|
+
"apply plugin: \"com.google.gms.google-services\""
|
|
1709
|
+
]];
|
|
1710
|
+
for (const [file, anchor, insertion] of edits) {
|
|
1711
|
+
if (relativePath !== file) continue;
|
|
1712
|
+
const next = insertAfterLine(rendered, anchor, insertion);
|
|
1713
|
+
if (next === rendered) {
|
|
1714
|
+
throw new Error(`[vxrn] prebuild template ${file} lost its ${anchor} anchor`);
|
|
1715
|
+
}
|
|
1716
|
+
rendered = next;
|
|
1717
|
+
}
|
|
1718
|
+
}
|
|
1719
|
+
if (platform === "android" && relativePath === "app/proguard-rules.pro" && app.android?.proguardRules) {
|
|
1720
|
+
rendered = `${rendered.trimEnd()}
|
|
1721
|
+
|
|
1722
|
+
${app.android.proguardRules.trim()}
|
|
1723
|
+
`;
|
|
1724
|
+
}
|
|
1568
1725
|
for (const [field, setting] of [
|
|
1569
1726
|
["minSdk", "minSdkVersion"],
|
|
1570
1727
|
["targetSdk", "targetSdkVersion"],
|
|
@@ -1640,6 +1797,12 @@ const generateForPlatform = async (root, platform, app, outDir = path.resolve(ro
|
|
|
1640
1797
|
platform,
|
|
1641
1798
|
app
|
|
1642
1799
|
});
|
|
1800
|
+
copyAppResources({
|
|
1801
|
+
root,
|
|
1802
|
+
dest,
|
|
1803
|
+
platform,
|
|
1804
|
+
app
|
|
1805
|
+
});
|
|
1643
1806
|
generateSceneDelegate({
|
|
1644
1807
|
dest,
|
|
1645
1808
|
platform,
|