@symbiote-native/cli 0.3.0 → 0.4.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.
- package/bundle.js +751 -430
- package/package.json +2 -2
- package/templates/layers/README.md +1 -1
- package/templates/layers/apple-authentication/package.json.fragment.json +5 -0
- package/templates/layers/blur/package.json.fragment.json +5 -0
- package/templates/layers/camera/package.json.fragment.json +5 -0
- package/templates/layers/gl/package.json.fragment.json +5 -0
- package/templates/layers/glass-effect/package.json.fragment.json +5 -0
- package/templates/layers/image/package.json.fragment.json +5 -0
- package/templates/layers/linear-gradient/package.json.fragment.json +5 -0
- package/templates/layers/live-photo/package.json.fragment.json +5 -0
- package/templates/layers/symbols/package.json.fragment.json +5 -0
- package/templates/layers/video/package.json.fragment.json +5 -0
package/bundle.js
CHANGED
|
@@ -3,9 +3,9 @@ import { createRequire } from "node:module";
|
|
|
3
3
|
import { styleText } from "node:util";
|
|
4
4
|
import process$1, { stdin, stdout } from "node:process";
|
|
5
5
|
import l__default from "node:readline";
|
|
6
|
-
import * as fs$
|
|
6
|
+
import * as fs$6 from "node:fs";
|
|
7
7
|
import { readFileSync } from "node:fs";
|
|
8
|
-
import * as path$
|
|
8
|
+
import * as path$5 from "node:path";
|
|
9
9
|
import { join } from "node:path";
|
|
10
10
|
import { fileURLToPath } from "node:url";
|
|
11
11
|
import { execFileSync } from "node:child_process";
|
|
@@ -132,6 +132,11 @@ const EXPO_PACKAGE_LAYERS = [
|
|
|
132
132
|
label: "App metrics",
|
|
133
133
|
symbiotePackage: "@symbiote-native/app-metrics"
|
|
134
134
|
},
|
|
135
|
+
{
|
|
136
|
+
id: "apple-authentication",
|
|
137
|
+
label: "Sign in with Apple",
|
|
138
|
+
symbiotePackage: "@symbiote-native/apple-authentication"
|
|
139
|
+
},
|
|
135
140
|
{
|
|
136
141
|
id: "application",
|
|
137
142
|
label: "Application info",
|
|
@@ -183,6 +188,11 @@ const EXPO_PACKAGE_LAYERS = [
|
|
|
183
188
|
label: "Blob",
|
|
184
189
|
symbiotePackage: "@symbiote-native/blob"
|
|
185
190
|
},
|
|
191
|
+
{
|
|
192
|
+
id: "blur",
|
|
193
|
+
label: "Blur",
|
|
194
|
+
symbiotePackage: "@symbiote-native/blur"
|
|
195
|
+
},
|
|
186
196
|
{
|
|
187
197
|
id: "brightness",
|
|
188
198
|
label: "Brightness",
|
|
@@ -193,6 +203,11 @@ const EXPO_PACKAGE_LAYERS = [
|
|
|
193
203
|
label: "Calendar",
|
|
194
204
|
symbiotePackage: "@symbiote-native/calendar"
|
|
195
205
|
},
|
|
206
|
+
{
|
|
207
|
+
id: "camera",
|
|
208
|
+
label: "Camera",
|
|
209
|
+
symbiotePackage: "@symbiote-native/camera"
|
|
210
|
+
},
|
|
196
211
|
{
|
|
197
212
|
id: "cellular",
|
|
198
213
|
label: "Cellular info",
|
|
@@ -238,11 +253,26 @@ const EXPO_PACKAGE_LAYERS = [
|
|
|
238
253
|
label: "Font",
|
|
239
254
|
symbiotePackage: "@symbiote-native/font"
|
|
240
255
|
},
|
|
256
|
+
{
|
|
257
|
+
id: "gl",
|
|
258
|
+
label: "GL",
|
|
259
|
+
symbiotePackage: "@symbiote-native/gl"
|
|
260
|
+
},
|
|
261
|
+
{
|
|
262
|
+
id: "glass-effect",
|
|
263
|
+
label: "Glass effect",
|
|
264
|
+
symbiotePackage: "@symbiote-native/glass-effect"
|
|
265
|
+
},
|
|
241
266
|
{
|
|
242
267
|
id: "haptics",
|
|
243
268
|
label: "Haptics",
|
|
244
269
|
symbiotePackage: "@symbiote-native/haptics"
|
|
245
270
|
},
|
|
271
|
+
{
|
|
272
|
+
id: "image",
|
|
273
|
+
label: "Image",
|
|
274
|
+
symbiotePackage: "@symbiote-native/image"
|
|
275
|
+
},
|
|
246
276
|
{
|
|
247
277
|
id: "image-manipulator",
|
|
248
278
|
label: "Image manipulator",
|
|
@@ -263,6 +293,16 @@ const EXPO_PACKAGE_LAYERS = [
|
|
|
263
293
|
label: "Keep awake",
|
|
264
294
|
symbiotePackage: "@symbiote-native/keep-awake"
|
|
265
295
|
},
|
|
296
|
+
{
|
|
297
|
+
id: "linear-gradient",
|
|
298
|
+
label: "Linear gradient",
|
|
299
|
+
symbiotePackage: "@symbiote-native/linear-gradient"
|
|
300
|
+
},
|
|
301
|
+
{
|
|
302
|
+
id: "live-photo",
|
|
303
|
+
label: "Live Photo",
|
|
304
|
+
symbiotePackage: "@symbiote-native/live-photo"
|
|
305
|
+
},
|
|
266
306
|
{
|
|
267
307
|
id: "local-auth",
|
|
268
308
|
label: "Local authentication",
|
|
@@ -369,6 +409,11 @@ const EXPO_PACKAGE_LAYERS = [
|
|
|
369
409
|
label: "Store review prompt",
|
|
370
410
|
symbiotePackage: "@symbiote-native/store-review"
|
|
371
411
|
},
|
|
412
|
+
{
|
|
413
|
+
id: "symbols",
|
|
414
|
+
label: "Symbols",
|
|
415
|
+
symbiotePackage: "@symbiote-native/symbols"
|
|
416
|
+
},
|
|
372
417
|
{
|
|
373
418
|
id: "system-ui",
|
|
374
419
|
label: "System UI",
|
|
@@ -384,6 +429,23 @@ const EXPO_PACKAGE_LAYERS = [
|
|
|
384
429
|
label: "Tracking transparency",
|
|
385
430
|
symbiotePackage: "@symbiote-native/tracking-transparency"
|
|
386
431
|
},
|
|
432
|
+
{
|
|
433
|
+
id: "video",
|
|
434
|
+
label: "Video",
|
|
435
|
+
symbiotePackage: "@symbiote-native/video",
|
|
436
|
+
optionalManifestBundles: [{
|
|
437
|
+
id: "backgroundPlayback",
|
|
438
|
+
label: "Background video playback",
|
|
439
|
+
warning: "Requesting FOREGROUND_SERVICE_MEDIA_PLAYBACK triggers Play Console policy review for a foreground service, Google asks for a justification before you can publish.",
|
|
440
|
+
nextSteps: "Set `staysActiveInBackground` and `showNowPlayingNotification` on the player. On iOS add `audio` to UIBackgroundModes in Info.plist yourself.",
|
|
441
|
+
manifestPermissions: ["android.permission.FOREGROUND_SERVICE", "android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK"],
|
|
442
|
+
manifestServices: [{
|
|
443
|
+
name: "expo.modules.video.playbackService.ExpoVideoPlaybackService",
|
|
444
|
+
foregroundServiceType: "mediaPlayback",
|
|
445
|
+
intentFilterActions: ["androidx.media3.session.MediaSessionService"]
|
|
446
|
+
}]
|
|
447
|
+
}]
|
|
448
|
+
},
|
|
387
449
|
{
|
|
388
450
|
id: "video-thumbnails",
|
|
389
451
|
label: "Video thumbnails",
|
|
@@ -1885,44 +1947,44 @@ const TEXT_FILE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
|
1885
1947
|
""
|
|
1886
1948
|
]);
|
|
1887
1949
|
function replaceTextContent(filePath, identity) {
|
|
1888
|
-
const content = fs$
|
|
1950
|
+
const content = fs$6.readFileSync(filePath, "utf8");
|
|
1889
1951
|
const replaced = content.split("com.canary").join(identity.bundleId).split("Canary").join(identity.nativeAppName);
|
|
1890
|
-
if (replaced !== content) fs$
|
|
1952
|
+
if (replaced !== content) fs$6.writeFileSync(filePath, replaced);
|
|
1891
1953
|
}
|
|
1892
1954
|
function walkTextFiles(dir, identity) {
|
|
1893
|
-
for (const entry of fs$
|
|
1894
|
-
const entryPath = path$
|
|
1955
|
+
for (const entry of fs$6.readdirSync(dir, { withFileTypes: true })) {
|
|
1956
|
+
const entryPath = path$5.join(dir, entry.name);
|
|
1895
1957
|
if (entry.isDirectory()) {
|
|
1896
1958
|
walkTextFiles(entryPath, identity);
|
|
1897
1959
|
continue;
|
|
1898
1960
|
}
|
|
1899
|
-
if (TEXT_FILE_EXTENSIONS.has(path$
|
|
1961
|
+
if (TEXT_FILE_EXTENSIONS.has(path$5.extname(entry.name))) replaceTextContent(entryPath, identity);
|
|
1900
1962
|
}
|
|
1901
1963
|
}
|
|
1902
1964
|
function renameCanaryPaths(dir, nativeAppName) {
|
|
1903
|
-
for (const entry of fs$
|
|
1904
|
-
for (const entry of fs$
|
|
1965
|
+
for (const entry of fs$6.readdirSync(dir, { withFileTypes: true })) if (entry.isDirectory()) renameCanaryPaths(path$5.join(dir, entry.name), nativeAppName);
|
|
1966
|
+
for (const entry of fs$6.readdirSync(dir, { withFileTypes: true })) {
|
|
1905
1967
|
if (!entry.name.startsWith("Canary")) continue;
|
|
1906
|
-
const from = path$
|
|
1907
|
-
const to = path$
|
|
1908
|
-
fs$
|
|
1968
|
+
const from = path$5.join(dir, entry.name);
|
|
1969
|
+
const to = path$5.join(dir, entry.name.replace("Canary", nativeAppName));
|
|
1970
|
+
fs$6.renameSync(from, to);
|
|
1909
1971
|
}
|
|
1910
1972
|
}
|
|
1911
1973
|
function moveAndroidPackageDir(javaSrcDir, bundleId) {
|
|
1912
|
-
const oldDir = path$
|
|
1913
|
-
if (!fs$
|
|
1914
|
-
const newDir = path$
|
|
1915
|
-
fs$
|
|
1916
|
-
fs$
|
|
1917
|
-
const oldTopDir = path$
|
|
1918
|
-
if (fs$
|
|
1974
|
+
const oldDir = path$5.join(javaSrcDir, "com", "canary");
|
|
1975
|
+
if (!fs$6.existsSync(oldDir)) return;
|
|
1976
|
+
const newDir = path$5.join(javaSrcDir, ...bundleId.split("."));
|
|
1977
|
+
fs$6.mkdirSync(path$5.dirname(newDir), { recursive: true });
|
|
1978
|
+
fs$6.renameSync(oldDir, newDir);
|
|
1979
|
+
const oldTopDir = path$5.join(javaSrcDir, "com");
|
|
1980
|
+
if (fs$6.existsSync(oldTopDir) && fs$6.readdirSync(oldTopDir).length === 0) fs$6.rmdirSync(oldTopDir);
|
|
1919
1981
|
}
|
|
1920
1982
|
function applyAppIdentity(root, identity) {
|
|
1921
1983
|
walkTextFiles(root, identity);
|
|
1922
|
-
const iosDir = path$
|
|
1923
|
-
if (fs$
|
|
1924
|
-
const androidAppSrc = path$
|
|
1925
|
-
if (fs$
|
|
1984
|
+
const iosDir = path$5.join(root, "ios");
|
|
1985
|
+
if (fs$6.existsSync(iosDir)) renameCanaryPaths(iosDir, identity.nativeAppName);
|
|
1986
|
+
const androidAppSrc = path$5.join(root, "android", "app", "src");
|
|
1987
|
+
if (fs$6.existsSync(androidAppSrc)) for (const sourceSet of fs$6.readdirSync(androidAppSrc)) moveAndroidPackageDir(path$5.join(androidAppSrc, sourceSet, "java"), identity.bundleId);
|
|
1926
1988
|
}
|
|
1927
1989
|
//#endregion
|
|
1928
1990
|
//#region src/utils/json.ts
|
|
@@ -1969,7 +2031,7 @@ function sortDependencies(packageJson) {
|
|
|
1969
2031
|
//#endregion
|
|
1970
2032
|
//#region src/utils/render-template.ts
|
|
1971
2033
|
function readJsonObject(filePath) {
|
|
1972
|
-
const parsed = JSON.parse(fs$
|
|
2034
|
+
const parsed = JSON.parse(fs$6.readFileSync(filePath, "utf8"));
|
|
1973
2035
|
if (!isJsonObject(parsed)) throw new Error(`${filePath}: expected a JSON object`);
|
|
1974
2036
|
return parsed;
|
|
1975
2037
|
}
|
|
@@ -1999,37 +2061,37 @@ function stripTypescriptInfix(filename) {
|
|
|
1999
2061
|
}
|
|
2000
2062
|
const DEFAULT_OPTIONS = { hasTypescript: true };
|
|
2001
2063
|
function renderTemplate(src, dest, options = DEFAULT_OPTIONS) {
|
|
2002
|
-
if (fs$
|
|
2003
|
-
fs$
|
|
2004
|
-
for (const entry of fs$
|
|
2064
|
+
if (fs$6.statSync(src).isDirectory()) {
|
|
2065
|
+
fs$6.mkdirSync(dest, { recursive: true });
|
|
2066
|
+
for (const entry of fs$6.readdirSync(src)) renderTemplate(path$5.resolve(src, entry), path$5.resolve(dest, entry), options);
|
|
2005
2067
|
return;
|
|
2006
2068
|
}
|
|
2007
|
-
const filename = path$
|
|
2069
|
+
const filename = path$5.basename(src);
|
|
2008
2070
|
if (filename === "README.md") return;
|
|
2009
2071
|
if (!options.hasTypescript && (isTypescriptOnlyFile(filename) || filename.includes(".typescript."))) return;
|
|
2010
2072
|
if (options.hasTypescript && isJavascriptOnlyFile(filename)) return;
|
|
2011
2073
|
const destFilename = filename.includes(".typescript.") ? stripTypescriptInfix(filename) : filename;
|
|
2012
|
-
const resolvedDest = path$
|
|
2074
|
+
const resolvedDest = path$5.join(path$5.dirname(dest), destFilename);
|
|
2013
2075
|
if (destFilename === "package.json.fragment.json") {
|
|
2014
|
-
const destPackageJsonPath = path$
|
|
2076
|
+
const destPackageJsonPath = path$5.resolve(path$5.dirname(resolvedDest), "package.json");
|
|
2015
2077
|
const fragment = readJsonObject(src);
|
|
2016
|
-
const merged = sortDependencies(deepMerge(fs$
|
|
2017
|
-
fs$
|
|
2078
|
+
const merged = sortDependencies(deepMerge(fs$6.existsSync(destPackageJsonPath) ? readJsonObject(destPackageJsonPath) : {}, fragment));
|
|
2079
|
+
fs$6.writeFileSync(destPackageJsonPath, `${JSON.stringify(merged, null, 2)}\n`);
|
|
2018
2080
|
return;
|
|
2019
2081
|
}
|
|
2020
|
-
if (destFilename === ".gitignore" && fs$
|
|
2021
|
-
const existing = fs$
|
|
2022
|
-
const addition = fs$
|
|
2023
|
-
fs$
|
|
2082
|
+
if (destFilename === ".gitignore" && fs$6.existsSync(resolvedDest)) {
|
|
2083
|
+
const existing = fs$6.readFileSync(resolvedDest, "utf8");
|
|
2084
|
+
const addition = fs$6.readFileSync(src, "utf8");
|
|
2085
|
+
fs$6.writeFileSync(resolvedDest, `${existing}\n${addition}`);
|
|
2024
2086
|
return;
|
|
2025
2087
|
}
|
|
2026
|
-
fs$
|
|
2088
|
+
fs$6.copyFileSync(src, resolvedDest);
|
|
2027
2089
|
}
|
|
2028
2090
|
//#endregion
|
|
2029
2091
|
//#region src/utils/apply-bootsplash-logo.ts
|
|
2030
2092
|
function applyBootsplashLogo(root, templatesRoot, framework) {
|
|
2031
|
-
const overlayRoot = path$
|
|
2032
|
-
if (fs$
|
|
2093
|
+
const overlayRoot = path$5.join(templatesRoot, "bootsplash-logo", framework);
|
|
2094
|
+
if (fs$6.existsSync(overlayRoot)) renderTemplate(overlayRoot, root);
|
|
2033
2095
|
}
|
|
2034
2096
|
//#endregion
|
|
2035
2097
|
//#region src/utils/apply-expo-modules-manifest.ts
|
|
@@ -2044,54 +2106,54 @@ const EXPO_MODULES_BACKUP_ATTRS = `android:supportsRtl="true"
|
|
|
2044
2106
|
android:dataExtractionRules="@xml/secure_store_data_extraction_rules"
|
|
2045
2107
|
android:fullBackupContent="@xml/secure_store_backup_rules">`;
|
|
2046
2108
|
function applyExpoModulesAndroidManifest(root) {
|
|
2047
|
-
const manifestPath = path$
|
|
2048
|
-
const manifest = fs$
|
|
2109
|
+
const manifestPath = path$5.join(root, "android/app/src/main/AndroidManifest.xml");
|
|
2110
|
+
const manifest = fs$6.readFileSync(manifestPath, "utf8");
|
|
2049
2111
|
const withPermissions = manifest.includes("USE_BIOMETRIC") ? manifest : manifest.replace(" <uses-permission android:name=\"android.permission.VIBRATE\" />", EXPO_MODULES_PERMISSIONS);
|
|
2050
2112
|
const withBackupAttrs = withPermissions.includes("dataExtractionRules") ? withPermissions : withPermissions.replace("android:supportsRtl=\"true\">", EXPO_MODULES_BACKUP_ATTRS);
|
|
2051
|
-
fs$
|
|
2113
|
+
fs$6.writeFileSync(manifestPath, withBackupAttrs);
|
|
2052
2114
|
}
|
|
2053
2115
|
//#endregion
|
|
2054
2116
|
//#region src/utils/apply-expo-modules-podfile-autolinking.ts
|
|
2055
2117
|
const AUTOLINKING_REQUIRE = "require File.join(File.dirname(`node --print \"require.resolve('expo/package.json')\"`), \"scripts/autolinking\")";
|
|
2056
2118
|
const NATIVE_MODULES_LINE = "config = use_native_modules!";
|
|
2057
2119
|
function applyExpoModulesPodfileAutolinking(root) {
|
|
2058
|
-
const podfilePath = path$
|
|
2059
|
-
if (!fs$
|
|
2060
|
-
let podfile = fs$
|
|
2120
|
+
const podfilePath = path$5.join(root, "ios", "Podfile");
|
|
2121
|
+
if (!fs$6.existsSync(podfilePath)) return;
|
|
2122
|
+
let podfile = fs$6.readFileSync(podfilePath, "utf8");
|
|
2061
2123
|
if (podfile.includes("use_expo_modules!")) return;
|
|
2062
2124
|
if (!podfile.includes(AUTOLINKING_REQUIRE)) podfile = `${AUTOLINKING_REQUIRE}\n\n${podfile}`;
|
|
2063
2125
|
podfile = podfile.replace(NATIVE_MODULES_LINE, `${NATIVE_MODULES_LINE}\n\n use_expo_modules!`);
|
|
2064
|
-
fs$
|
|
2126
|
+
fs$6.writeFileSync(podfilePath, podfile);
|
|
2065
2127
|
}
|
|
2066
2128
|
//#endregion
|
|
2067
2129
|
//#region src/utils/apply-expo-modules-podfile.ts
|
|
2068
2130
|
const DEFAULT_PLATFORM_LINE = "platform :ios, min_ios_version_supported.to_s";
|
|
2069
2131
|
const RAISED_PLATFORM_LINE = "platform :ios, [min_ios_version_supported.to_f, 16.4].max.to_s";
|
|
2070
2132
|
function applyExpoModulesPodfilePlatform(root) {
|
|
2071
|
-
const podfilePath = path$
|
|
2072
|
-
if (!fs$
|
|
2073
|
-
const podfile = fs$
|
|
2133
|
+
const podfilePath = path$5.join(root, "ios", "Podfile");
|
|
2134
|
+
if (!fs$6.existsSync(podfilePath)) return;
|
|
2135
|
+
const podfile = fs$6.readFileSync(podfilePath, "utf8");
|
|
2074
2136
|
if (!podfile.includes(DEFAULT_PLATFORM_LINE)) return;
|
|
2075
|
-
fs$
|
|
2137
|
+
fs$6.writeFileSync(podfilePath, podfile.replace(DEFAULT_PLATFORM_LINE, RAISED_PLATFORM_LINE));
|
|
2076
2138
|
}
|
|
2077
2139
|
//#endregion
|
|
2078
2140
|
//#region src/utils/apply-expo-modules-xcode-deployment-target.ts
|
|
2079
2141
|
const MIN_EXPO_IOS_VERSION = 16.4;
|
|
2080
2142
|
const DEPLOYMENT_TARGET_LINE = /IPHONEOS_DEPLOYMENT_TARGET = (\d+(?:\.\d+)?);/g;
|
|
2081
2143
|
function findPbxprojPath(root) {
|
|
2082
|
-
const iosDir = path$
|
|
2083
|
-
if (!fs$
|
|
2084
|
-
const xcodeproj = fs$
|
|
2144
|
+
const iosDir = path$5.join(root, "ios");
|
|
2145
|
+
if (!fs$6.existsSync(iosDir)) return void 0;
|
|
2146
|
+
const xcodeproj = fs$6.readdirSync(iosDir).find((entry) => entry.endsWith(".xcodeproj"));
|
|
2085
2147
|
if (!xcodeproj) return void 0;
|
|
2086
|
-
const pbxprojPath = path$
|
|
2087
|
-
return fs$
|
|
2148
|
+
const pbxprojPath = path$5.join(iosDir, xcodeproj, "project.pbxproj");
|
|
2149
|
+
return fs$6.existsSync(pbxprojPath) ? pbxprojPath : void 0;
|
|
2088
2150
|
}
|
|
2089
2151
|
function applyExpoModulesXcodeDeploymentTarget(root) {
|
|
2090
2152
|
const pbxprojPath = findPbxprojPath(root);
|
|
2091
2153
|
if (!pbxprojPath) return;
|
|
2092
|
-
const pbxproj = fs$
|
|
2154
|
+
const pbxproj = fs$6.readFileSync(pbxprojPath, "utf8");
|
|
2093
2155
|
const raised = pbxproj.replace(DEPLOYMENT_TARGET_LINE, (line, version) => Number.parseFloat(version) < MIN_EXPO_IOS_VERSION ? `IPHONEOS_DEPLOYMENT_TARGET = ${MIN_EXPO_IOS_VERSION};` : line);
|
|
2094
|
-
if (raised !== pbxproj) fs$
|
|
2156
|
+
if (raised !== pbxproj) fs$6.writeFileSync(pbxprojPath, raised);
|
|
2095
2157
|
}
|
|
2096
2158
|
//#endregion
|
|
2097
2159
|
//#region src/utils/apply-splash-screen-hide.ts
|
|
@@ -2108,8 +2170,8 @@ const APP_COMPONENT_FILENAMES = /* @__PURE__ */ new Set([
|
|
|
2108
2170
|
"App.svelte"
|
|
2109
2171
|
]);
|
|
2110
2172
|
function findAppComponentFile(dir) {
|
|
2111
|
-
for (const entry of fs$
|
|
2112
|
-
const entryPath = path$
|
|
2173
|
+
for (const entry of fs$6.readdirSync(dir, { withFileTypes: true })) {
|
|
2174
|
+
const entryPath = path$5.join(dir, entry.name);
|
|
2113
2175
|
if (entry.isDirectory()) {
|
|
2114
2176
|
if (SKIPPED_DIR_NAMES$1.has(entry.name)) continue;
|
|
2115
2177
|
const found = findAppComponentFile(entryPath);
|
|
@@ -2155,9 +2217,9 @@ function patchSvelte(source) {
|
|
|
2155
2217
|
function applySplashScreenAppHide(root, options) {
|
|
2156
2218
|
const appFilePath = findAppComponentFile(root);
|
|
2157
2219
|
if (appFilePath === void 0) return;
|
|
2158
|
-
const source = fs$
|
|
2220
|
+
const source = fs$6.readFileSync(appFilePath, "utf8");
|
|
2159
2221
|
if (/\bhide\(\)/.test(source)) return;
|
|
2160
|
-
fs$
|
|
2222
|
+
fs$6.writeFileSync(appFilePath, patch(source, options.framework, appFilePath));
|
|
2161
2223
|
}
|
|
2162
2224
|
function patch(source, framework, appFilePath) {
|
|
2163
2225
|
switch (framework) {
|
|
@@ -2173,16 +2235,16 @@ function patch(source, framework, appFilePath) {
|
|
|
2173
2235
|
const INLINE_STYLE_BLOCK_FRAMEWORKS = /* @__PURE__ */ new Set(["vue-sfc", "svelte"]);
|
|
2174
2236
|
const STYLE_TAG_PATTERN = /<style(?=[ >])/;
|
|
2175
2237
|
function addStyleLangAttribute(dir, lang) {
|
|
2176
|
-
for (const entry of fs$
|
|
2177
|
-
const entryPath = path$
|
|
2238
|
+
for (const entry of fs$6.readdirSync(dir, { withFileTypes: true })) {
|
|
2239
|
+
const entryPath = path$5.join(dir, entry.name);
|
|
2178
2240
|
if (entry.isDirectory()) {
|
|
2179
2241
|
if (SKIPPED_DIR_NAMES.has(entry.name)) continue;
|
|
2180
2242
|
addStyleLangAttribute(entryPath, lang);
|
|
2181
2243
|
continue;
|
|
2182
2244
|
}
|
|
2183
2245
|
if (!entry.name.endsWith(".vue") && !entry.name.endsWith(".svelte")) continue;
|
|
2184
|
-
const content = fs$
|
|
2185
|
-
if (STYLE_TAG_PATTERN.test(content)) fs$
|
|
2246
|
+
const content = fs$6.readFileSync(entryPath, "utf8");
|
|
2247
|
+
if (STYLE_TAG_PATTERN.test(content)) fs$6.writeFileSync(entryPath, content.replace(STYLE_TAG_PATTERN, `<style lang="${lang}"`));
|
|
2186
2248
|
}
|
|
2187
2249
|
}
|
|
2188
2250
|
const PREPROCESSOR_EXTENSIONS = {
|
|
@@ -2196,42 +2258,42 @@ const SKIPPED_DIR_NAMES = /* @__PURE__ */ new Set([
|
|
|
2196
2258
|
"node_modules"
|
|
2197
2259
|
]);
|
|
2198
2260
|
function findAppCssFile(dir) {
|
|
2199
|
-
for (const entry of fs$
|
|
2261
|
+
for (const entry of fs$6.readdirSync(dir, { withFileTypes: true })) {
|
|
2200
2262
|
if (entry.isDirectory()) {
|
|
2201
2263
|
if (SKIPPED_DIR_NAMES.has(entry.name)) continue;
|
|
2202
|
-
const found = findAppCssFile(path$
|
|
2264
|
+
const found = findAppCssFile(path$5.join(dir, entry.name));
|
|
2203
2265
|
if (found !== void 0) return found;
|
|
2204
2266
|
continue;
|
|
2205
2267
|
}
|
|
2206
|
-
if (entry.name === "App.css") return path$
|
|
2268
|
+
if (entry.name === "App.css") return path$5.join(dir, entry.name);
|
|
2207
2269
|
}
|
|
2208
2270
|
}
|
|
2209
2271
|
function replaceInTextFiles(dir, from, to) {
|
|
2210
|
-
for (const entry of fs$
|
|
2211
|
-
const entryPath = path$
|
|
2272
|
+
for (const entry of fs$6.readdirSync(dir, { withFileTypes: true })) {
|
|
2273
|
+
const entryPath = path$5.join(dir, entry.name);
|
|
2212
2274
|
if (entry.isDirectory()) {
|
|
2213
2275
|
if (SKIPPED_DIR_NAMES.has(entry.name)) continue;
|
|
2214
2276
|
replaceInTextFiles(entryPath, from, to);
|
|
2215
2277
|
continue;
|
|
2216
2278
|
}
|
|
2217
|
-
const content = fs$
|
|
2218
|
-
if (content.includes(from)) fs$
|
|
2279
|
+
const content = fs$6.readFileSync(entryPath, "utf8");
|
|
2280
|
+
if (content.includes(from)) fs$6.writeFileSync(entryPath, content.split(from).join(to));
|
|
2219
2281
|
}
|
|
2220
2282
|
}
|
|
2221
2283
|
function renameStylesheetExtension(root, extension) {
|
|
2222
2284
|
const cssFile = findAppCssFile(root);
|
|
2223
2285
|
if (cssFile === void 0) return;
|
|
2224
|
-
fs$
|
|
2286
|
+
fs$6.renameSync(cssFile, path$5.join(path$5.dirname(cssFile), `App.${extension}`));
|
|
2225
2287
|
replaceInTextFiles(root, "'./App.css'", `'./App.${extension}'`);
|
|
2226
2288
|
}
|
|
2227
2289
|
function applyStyling(root, options) {
|
|
2228
2290
|
const { styling, jsTemplateDir, hasNavigation, hasTypescript, templatesRoot } = options;
|
|
2229
2291
|
if (styling === "css") return;
|
|
2230
2292
|
if (styling === "css-modules" || styling === "stylesheet") {
|
|
2231
|
-
renderTemplate(path$
|
|
2232
|
-
if (hasNavigation) renderTemplate(path$
|
|
2293
|
+
renderTemplate(path$5.join(templatesRoot, "styling", "js", jsTemplateDir, styling), root, { hasTypescript });
|
|
2294
|
+
if (hasNavigation) renderTemplate(path$5.join(templatesRoot, "styling", "navigation", jsTemplateDir, styling), root, { hasTypescript });
|
|
2233
2295
|
const orphanedCss = findAppCssFile(root);
|
|
2234
|
-
if (orphanedCss !== void 0) fs$
|
|
2296
|
+
if (orphanedCss !== void 0) fs$6.rmSync(orphanedCss, { force: true });
|
|
2235
2297
|
return;
|
|
2236
2298
|
}
|
|
2237
2299
|
if (INLINE_STYLE_BLOCK_FRAMEWORKS.has(jsTemplateDir)) {
|
|
@@ -2270,50 +2332,50 @@ var TargetDirNotEmptyError = class extends Error {
|
|
|
2270
2332
|
}
|
|
2271
2333
|
};
|
|
2272
2334
|
function resolveTemplatesRoot() {
|
|
2273
|
-
const packageDir = path$
|
|
2274
|
-
const sibling = path$
|
|
2275
|
-
return fs$
|
|
2335
|
+
const packageDir = path$5.dirname(fileURLToPath(import.meta.url));
|
|
2336
|
+
const sibling = path$5.join(packageDir, "templates");
|
|
2337
|
+
return fs$6.existsSync(sibling) ? sibling : path$5.resolve(packageDir, "../templates");
|
|
2276
2338
|
}
|
|
2277
2339
|
const templatesRoot = resolveTemplatesRoot();
|
|
2278
2340
|
function isEmptyDir(dir) {
|
|
2279
|
-
if (!fs$
|
|
2280
|
-
const entries = fs$
|
|
2341
|
+
if (!fs$6.existsSync(dir)) return true;
|
|
2342
|
+
const entries = fs$6.readdirSync(dir);
|
|
2281
2343
|
return entries.length === 0 || entries.length === 1 && entries[0] === ".git";
|
|
2282
2344
|
}
|
|
2283
2345
|
function scaffoldApp(options) {
|
|
2284
|
-
const root = path$
|
|
2346
|
+
const root = path$5.resolve(process.cwd(), options.appName);
|
|
2285
2347
|
const jsTemplateDir = resolveJsTemplateDir(options.framework, options.vueFlavor);
|
|
2286
2348
|
if (!isEmptyDir(root)) throw new TargetDirNotEmptyError(root);
|
|
2287
|
-
fs$
|
|
2288
|
-
const packageNameSource = options.appName === "." ? path$
|
|
2289
|
-
fs$
|
|
2349
|
+
fs$6.mkdirSync(root, { recursive: true });
|
|
2350
|
+
const packageNameSource = options.appName === "." ? path$5.basename(root) : options.appName;
|
|
2351
|
+
fs$6.writeFileSync(path$5.join(root, "package.json"), `${JSON.stringify({
|
|
2290
2352
|
name: toValidPackageName(packageNameSource),
|
|
2291
2353
|
version: "0.0.1",
|
|
2292
2354
|
private: true
|
|
2293
2355
|
}, null, 2)}\n`);
|
|
2294
|
-
renderTemplate(path$
|
|
2295
|
-
renderTemplate(path$
|
|
2356
|
+
renderTemplate(path$5.join(templatesRoot, "native"), root, { hasTypescript: options.hasTypescript });
|
|
2357
|
+
renderTemplate(path$5.join(templatesRoot, "js", jsTemplateDir), root, { hasTypescript: options.hasTypescript });
|
|
2296
2358
|
applyBootsplashLogo(root, templatesRoot, options.framework);
|
|
2297
2359
|
const hasExpoModules = options.hasExpoModules || (options.expoPackages?.size ?? 0) > 0;
|
|
2298
2360
|
if (options.hasNavigation) {
|
|
2299
|
-
const navigationLayerRoot = path$
|
|
2300
|
-
renderTemplate(path$
|
|
2301
|
-
renderTemplate(path$
|
|
2361
|
+
const navigationLayerRoot = path$5.join(templatesRoot, "layers", "navigation");
|
|
2362
|
+
renderTemplate(path$5.join(navigationLayerRoot, "package.json.fragment.json"), path$5.join(root, "package.json.fragment.json"));
|
|
2363
|
+
renderTemplate(path$5.join(navigationLayerRoot, "app", jsTemplateDir), root, { hasTypescript: options.hasTypescript });
|
|
2302
2364
|
}
|
|
2303
|
-
if (hasExpoModules) renderTemplate(path$
|
|
2365
|
+
if (hasExpoModules) renderTemplate(path$5.join(templatesRoot, "layers", "expo-modules"), root);
|
|
2304
2366
|
if (options.hasSplashScreen) {
|
|
2305
|
-
const splashScreenLayerRoot = path$
|
|
2306
|
-
renderTemplate(path$
|
|
2307
|
-
renderTemplate(path$
|
|
2367
|
+
const splashScreenLayerRoot = path$5.join(templatesRoot, "layers", "splash-screen");
|
|
2368
|
+
renderTemplate(path$5.join(splashScreenLayerRoot, "package.json.fragment.json"), path$5.join(root, "package.json.fragment.json"));
|
|
2369
|
+
renderTemplate(path$5.join(splashScreenLayerRoot, "native"), root);
|
|
2308
2370
|
}
|
|
2309
|
-
if (options.hasSlider) renderTemplate(path$
|
|
2310
|
-
for (const layerName of options.expoPackages ?? []) renderTemplate(path$
|
|
2371
|
+
if (options.hasSlider) renderTemplate(path$5.join(templatesRoot, "layers", "slider", "package.json.fragment.json"), path$5.join(root, "package.json.fragment.json"));
|
|
2372
|
+
for (const layerName of options.expoPackages ?? []) renderTemplate(path$5.join(templatesRoot, "layers", layerName, "package.json.fragment.json"), path$5.join(root, "package.json.fragment.json"));
|
|
2311
2373
|
if (options.hasTesting) {
|
|
2312
|
-
const testingLayerRoot = path$
|
|
2374
|
+
const testingLayerRoot = path$5.join(templatesRoot, "layers", "testing");
|
|
2313
2375
|
const fragmentName = options.framework === "angular" ? "angular" : "base";
|
|
2314
|
-
renderTemplate(path$
|
|
2315
|
-
renderTemplate(path$
|
|
2316
|
-
renderTemplate(path$
|
|
2376
|
+
renderTemplate(path$5.join(testingLayerRoot, "fragment", fragmentName, "package.json.fragment.json"), path$5.join(root, "package.json.fragment.json"));
|
|
2377
|
+
renderTemplate(path$5.join(testingLayerRoot, "detox.config.js"), path$5.join(root, "detox.config.js"));
|
|
2378
|
+
renderTemplate(path$5.join(testingLayerRoot, "e2e"), path$5.join(root, "e2e"), { hasTypescript: true });
|
|
2317
2379
|
}
|
|
2318
2380
|
if (hasExpoModules) {
|
|
2319
2381
|
applyExpoModulesAndroidManifest(root);
|
|
@@ -2338,9 +2400,9 @@ function scaffoldApp(options) {
|
|
|
2338
2400
|
//#endregion
|
|
2339
2401
|
//#region src/utils/find-ios-app-dir.ts
|
|
2340
2402
|
function findIosAppDir(root) {
|
|
2341
|
-
const iosDir = path$
|
|
2342
|
-
if (!fs$
|
|
2343
|
-
for (const entry of fs$
|
|
2403
|
+
const iosDir = path$5.join(root, "ios");
|
|
2404
|
+
if (!fs$6.existsSync(iosDir)) return void 0;
|
|
2405
|
+
for (const entry of fs$6.readdirSync(iosDir, { withFileTypes: true })) if (entry.isDirectory() && entry.name !== "Pods") return path$5.join(iosDir, entry.name);
|
|
2344
2406
|
}
|
|
2345
2407
|
//#endregion
|
|
2346
2408
|
//#region src/utils/apply-expo-modules-plist.ts
|
|
@@ -2352,14 +2414,14 @@ const USAGE_DESCRIPTIONS = [
|
|
|
2352
2414
|
function applyExpoModulesInfoPlist(root) {
|
|
2353
2415
|
const iosAppDir = findIosAppDir(root);
|
|
2354
2416
|
if (iosAppDir === void 0) return;
|
|
2355
|
-
const plistPath = path$
|
|
2356
|
-
if (!fs$
|
|
2357
|
-
const plist = fs$
|
|
2417
|
+
const plistPath = path$5.join(iosAppDir, "Info.plist");
|
|
2418
|
+
if (!fs$6.existsSync(plistPath)) return;
|
|
2419
|
+
const plist = fs$6.readFileSync(plistPath, "utf8");
|
|
2358
2420
|
const updated = USAGE_DESCRIPTIONS.reduce((current, [key, description]) => {
|
|
2359
2421
|
if (current.includes(`<key>${key}</key>`)) return current;
|
|
2360
2422
|
return current.replace("</dict>\n</plist>", `\t<key>${key}</key>\n\t<string>${description}</string>\n</dict>\n</plist>`);
|
|
2361
2423
|
}, plist);
|
|
2362
|
-
if (updated !== plist) fs$
|
|
2424
|
+
if (updated !== plist) fs$6.writeFileSync(plistPath, updated);
|
|
2363
2425
|
}
|
|
2364
2426
|
//#endregion
|
|
2365
2427
|
//#region src/utils/apply-splash-screen-app-delegate.ts
|
|
@@ -2368,9 +2430,9 @@ const REACT_IMPORT = "import React\n";
|
|
|
2368
2430
|
function applySplashScreenAppDelegate(root) {
|
|
2369
2431
|
const iosAppDir = findIosAppDir(root);
|
|
2370
2432
|
if (iosAppDir === void 0) return;
|
|
2371
|
-
const appDelegatePath = path$
|
|
2372
|
-
if (!fs$
|
|
2373
|
-
const source = fs$
|
|
2433
|
+
const appDelegatePath = path$5.join(iosAppDir, "AppDelegate.swift");
|
|
2434
|
+
if (!fs$6.existsSync(appDelegatePath)) return;
|
|
2435
|
+
const source = fs$6.readFileSync(appDelegatePath, "utf8");
|
|
2374
2436
|
if (source.includes("RNBootSplash.initWithStoryboard(")) return;
|
|
2375
2437
|
if (!source.includes(REACT_IMPORT)) return;
|
|
2376
2438
|
const updated = source.includes("import RNBootSplash") ? source : source.replace(REACT_IMPORT, `${REACT_IMPORT}import RNBootSplash\n`);
|
|
@@ -2379,13 +2441,13 @@ function applySplashScreenAppDelegate(root) {
|
|
|
2379
2441
|
}
|
|
2380
2442
|
|
|
2381
2443
|
`);
|
|
2382
|
-
fs$
|
|
2444
|
+
fs$6.writeFileSync(appDelegatePath, withInit);
|
|
2383
2445
|
}
|
|
2384
2446
|
//#endregion
|
|
2385
2447
|
//#region src/utils/apply-splash-screen-main-activity.ts
|
|
2386
2448
|
function findMainActivityKt(dir) {
|
|
2387
|
-
for (const entry of fs$
|
|
2388
|
-
const entryPath = path$
|
|
2449
|
+
for (const entry of fs$6.readdirSync(dir, { withFileTypes: true })) {
|
|
2450
|
+
const entryPath = path$5.join(dir, entry.name);
|
|
2389
2451
|
if (entry.isDirectory()) {
|
|
2390
2452
|
const found = findMainActivityKt(entryPath);
|
|
2391
2453
|
if (found !== void 0) return found;
|
|
@@ -2397,32 +2459,32 @@ function findMainActivityKt(dir) {
|
|
|
2397
2459
|
const ONCREATE_OPEN = /override fun onCreate\(savedInstanceState: Bundle\?\) \{\n/;
|
|
2398
2460
|
const REACT_ACTIVITY_IMPORT = "import com.facebook.react.ReactActivity\n";
|
|
2399
2461
|
function applySplashScreenMainActivity(root) {
|
|
2400
|
-
const javaDir = path$
|
|
2401
|
-
if (!fs$
|
|
2462
|
+
const javaDir = path$5.join(root, "android/app/src/main/java");
|
|
2463
|
+
if (!fs$6.existsSync(javaDir)) return;
|
|
2402
2464
|
const mainActivityPath = findMainActivityKt(javaDir);
|
|
2403
2465
|
if (mainActivityPath === void 0) return;
|
|
2404
|
-
const source = fs$
|
|
2466
|
+
const source = fs$6.readFileSync(mainActivityPath, "utf8");
|
|
2405
2467
|
if (source.includes("RNBootSplash.init(")) return;
|
|
2406
2468
|
if (!source.includes(REACT_ACTIVITY_IMPORT)) return;
|
|
2407
2469
|
let updated = source.includes("import com.zoontek.rnbootsplash.RNBootSplash") ? source : source.replace(REACT_ACTIVITY_IMPORT, `${REACT_ACTIVITY_IMPORT}import com.zoontek.rnbootsplash.RNBootSplash\n`);
|
|
2408
2470
|
if (!updated.includes("import android.os.Bundle")) updated = updated.replace(REACT_ACTIVITY_IMPORT, `import android.os.Bundle\n${REACT_ACTIVITY_IMPORT}`);
|
|
2409
2471
|
updated = ONCREATE_OPEN.test(updated) ? updated.replace(ONCREATE_OPEN, (match) => `${match} RNBootSplash.init(this, R.style.BootTheme)\n`) : updated.replace("class MainActivity : ReactActivity() {\n", "class MainActivity : ReactActivity() {\n\n override fun onCreate(savedInstanceState: Bundle?) {\n RNBootSplash.init(this, R.style.BootTheme)\n super.onCreate(savedInstanceState)\n }\n");
|
|
2410
|
-
fs$
|
|
2472
|
+
fs$6.writeFileSync(mainActivityPath, updated);
|
|
2411
2473
|
}
|
|
2412
2474
|
//#endregion
|
|
2413
2475
|
//#region src/utils/apply-splash-screen-manifest.ts
|
|
2414
2476
|
const MAIN_ACTIVITY_BLOCK = /<activity\b[^>]*android:name="\.MainActivity"[^>]*>/;
|
|
2415
2477
|
function applySplashScreenManifest(root) {
|
|
2416
|
-
const manifestPath = path$
|
|
2417
|
-
if (!fs$
|
|
2418
|
-
const manifest = fs$
|
|
2478
|
+
const manifestPath = path$5.join(root, "android/app/src/main/AndroidManifest.xml");
|
|
2479
|
+
if (!fs$6.existsSync(manifestPath)) return;
|
|
2480
|
+
const manifest = fs$6.readFileSync(manifestPath, "utf8");
|
|
2419
2481
|
const match = manifest.match(MAIN_ACTIVITY_BLOCK);
|
|
2420
2482
|
if (match === null) return;
|
|
2421
2483
|
const block = match[0];
|
|
2422
2484
|
if (block.includes("@style/BootTheme")) return;
|
|
2423
2485
|
if (!block.includes("android:theme=\"@style/AppTheme\"")) return;
|
|
2424
2486
|
const updatedBlock = block.replace("android:theme=\"@style/AppTheme\"", "android:theme=\"@style/BootTheme\"");
|
|
2425
|
-
fs$
|
|
2487
|
+
fs$6.writeFileSync(manifestPath, manifest.replace(block, updatedBlock));
|
|
2426
2488
|
}
|
|
2427
2489
|
//#endregion
|
|
2428
2490
|
//#region src/utils/apply-splash-screen-styles.ts
|
|
@@ -2433,22 +2495,22 @@ const BOOT_THEME_STYLE = ` <style name="BootTheme" parent="Theme.BootSplash">
|
|
|
2433
2495
|
</style>
|
|
2434
2496
|
`;
|
|
2435
2497
|
function applySplashScreenStyles(root) {
|
|
2436
|
-
const stylesPath = path$
|
|
2437
|
-
if (!fs$
|
|
2438
|
-
const styles = fs$
|
|
2498
|
+
const stylesPath = path$5.join(root, "android/app/src/main/res/values/styles.xml");
|
|
2499
|
+
if (!fs$6.existsSync(stylesPath)) return;
|
|
2500
|
+
const styles = fs$6.readFileSync(stylesPath, "utf8");
|
|
2439
2501
|
if (styles.includes("name=\"BootTheme\"")) return;
|
|
2440
2502
|
if (!styles.includes("</resources>")) return;
|
|
2441
|
-
fs$
|
|
2503
|
+
fs$6.writeFileSync(stylesPath, styles.replace("</resources>", `${BOOT_THEME_STYLE}</resources>`));
|
|
2442
2504
|
}
|
|
2443
2505
|
//#endregion
|
|
2444
2506
|
//#region src/add-layers.ts
|
|
2445
2507
|
function applyDependencyOnlyLayer(root, layerName) {
|
|
2446
|
-
renderTemplate(path$
|
|
2508
|
+
renderTemplate(path$5.join(templatesRoot, "layers", layerName, "package.json.fragment.json"), path$5.join(root, "package.json.fragment.json"));
|
|
2447
2509
|
}
|
|
2448
2510
|
function applyExpoModulesLayer(root) {
|
|
2449
2511
|
applyDependencyOnlyLayer(root, "expo-modules");
|
|
2450
|
-
if (fs$
|
|
2451
|
-
if (fs$
|
|
2512
|
+
if (fs$6.existsSync(path$5.join(root, "android/app/src/main/AndroidManifest.xml"))) applyExpoModulesAndroidManifest(root);
|
|
2513
|
+
if (fs$6.existsSync(path$5.join(root, "ios"))) {
|
|
2452
2514
|
applyExpoModulesInfoPlist(root);
|
|
2453
2515
|
applyExpoModulesPodfilePlatform(root);
|
|
2454
2516
|
applyExpoModulesPodfileAutolinking(root);
|
|
@@ -2465,14 +2527,14 @@ function applySplashScreenLayer(root, framework) {
|
|
|
2465
2527
|
applyBootsplashLogo(root, templatesRoot, framework);
|
|
2466
2528
|
}
|
|
2467
2529
|
async function applyTestingLayer(root, framework, confirmOverwrite) {
|
|
2468
|
-
const testingLayerRoot = path$
|
|
2530
|
+
const testingLayerRoot = path$5.join(templatesRoot, "layers", "testing");
|
|
2469
2531
|
const fragmentName = framework === "angular" ? "angular" : "base";
|
|
2470
|
-
renderTemplate(path$
|
|
2471
|
-
const detoxConfigPath = path$
|
|
2472
|
-
const e2eDirPath = path$
|
|
2473
|
-
if ((fs$
|
|
2474
|
-
renderTemplate(path$
|
|
2475
|
-
renderTemplate(path$
|
|
2532
|
+
renderTemplate(path$5.join(testingLayerRoot, "fragment", fragmentName, "package.json.fragment.json"), path$5.join(root, "package.json.fragment.json"));
|
|
2533
|
+
const detoxConfigPath = path$5.join(root, "detox.config.js");
|
|
2534
|
+
const e2eDirPath = path$5.join(root, "e2e");
|
|
2535
|
+
if ((fs$6.existsSync(detoxConfigPath) || fs$6.existsSync(e2eDirPath)) && !await confirmOverwrite()) return false;
|
|
2536
|
+
renderTemplate(path$5.join(testingLayerRoot, "detox.config.js"), detoxConfigPath);
|
|
2537
|
+
renderTemplate(path$5.join(testingLayerRoot, "e2e"), e2eDirPath, { hasTypescript: true });
|
|
2476
2538
|
return true;
|
|
2477
2539
|
}
|
|
2478
2540
|
async function addLayersToApp(options) {
|
|
@@ -2547,105 +2609,114 @@ function getCommand(packageManager, script) {
|
|
|
2547
2609
|
return packageManager === "npm" ? `npm run ${script}` : `${packageManager} ${script}`;
|
|
2548
2610
|
}
|
|
2549
2611
|
//#endregion
|
|
2550
|
-
//#region src/
|
|
2551
|
-
var
|
|
2552
|
-
const fs = __require("node:fs");
|
|
2553
|
-
const path = __require("node:path");
|
|
2554
|
-
const
|
|
2555
|
-
const END_TAG = "SYMBIOTE-EXPO-LINK:END";
|
|
2556
|
-
const REGION_NOTE = "generated by symbiote-expo-link - do not edit, regenerated on every install";
|
|
2557
|
-
const REACT_ANDROID_ANCHOR = "implementation(\"com.facebook.react:react-android\")";
|
|
2558
|
-
const MODULES_MAP_ANCHOR = "override fun getModulesMap(): Map<Class<out Module>, String?> = mapOf(";
|
|
2559
|
-
const MODULES_PROVIDER_CLASS_ANCHOR = ": ModulesProvider {";
|
|
2560
|
-
const MANIFEST_FILENAME = "native-link.json";
|
|
2612
|
+
//#region ../expo-modules-link/src/common.cjs
|
|
2613
|
+
var require_common = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
2614
|
+
const fs$5 = __require("node:fs");
|
|
2615
|
+
const path$4 = __require("node:path");
|
|
2616
|
+
const DEBUG_ENV_VALUES = ["1", "true"];
|
|
2561
2617
|
function isDebug() {
|
|
2562
|
-
return
|
|
2618
|
+
return DEBUG_ENV_VALUES.includes(process.env.DEBUG) || globalThis.__SYMBIOTE_DEBUG__ === true;
|
|
2563
2619
|
}
|
|
2564
2620
|
function dlog(...args) {
|
|
2565
|
-
if (isDebug())
|
|
2621
|
+
if (isDebug()) process.stderr.write(`[symbiote-expo-link] ${args.join(" ")}\n`);
|
|
2566
2622
|
}
|
|
2567
2623
|
function warn(message) {
|
|
2568
2624
|
console.warn(`[symbiote-expo-link] ${message}`);
|
|
2569
2625
|
}
|
|
2570
2626
|
function writeFileAtomic(filePath, content) {
|
|
2571
2627
|
const tmpPath = `${filePath}.symbiote-expo-link.${process.pid}.tmp`;
|
|
2572
|
-
fs.writeFileSync(tmpPath, content);
|
|
2573
|
-
fs.renameSync(tmpPath, filePath);
|
|
2628
|
+
fs$5.writeFileSync(tmpPath, content);
|
|
2629
|
+
fs$5.renameSync(tmpPath, filePath);
|
|
2574
2630
|
}
|
|
2575
|
-
function
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
if (findInfoPlistFile(candidate)) return candidate;
|
|
2580
|
-
}
|
|
2581
|
-
return null;
|
|
2631
|
+
function applyIfChanged(filePath, content, nextContent) {
|
|
2632
|
+
if (nextContent === null || nextContent === content) return;
|
|
2633
|
+
writeFileAtomic(filePath, nextContent);
|
|
2634
|
+
dlog(`updated ${filePath}`);
|
|
2582
2635
|
}
|
|
2583
|
-
function
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2636
|
+
function escapeXml(value) {
|
|
2637
|
+
return String(value).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
2638
|
+
}
|
|
2639
|
+
function findFileInTree(root, fileName, skip = null) {
|
|
2640
|
+
if (!fs$5.existsSync(root)) return null;
|
|
2641
|
+
const stack = [root];
|
|
2588
2642
|
while (stack.length > 0) {
|
|
2589
2643
|
const dir = stack.pop();
|
|
2590
|
-
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
2591
|
-
if (skip.test(entry.name)) continue;
|
|
2592
|
-
const full = path.join(dir, entry.name);
|
|
2644
|
+
for (const entry of fs$5.readdirSync(dir, { withFileTypes: true })) {
|
|
2645
|
+
if (skip && skip.test(entry.name)) continue;
|
|
2646
|
+
const full = path$4.join(dir, entry.name);
|
|
2593
2647
|
if (entry.isDirectory()) stack.push(full);
|
|
2594
|
-
else if (entry.name ===
|
|
2648
|
+
else if (entry.name === fileName) return full;
|
|
2595
2649
|
}
|
|
2596
2650
|
}
|
|
2597
2651
|
return null;
|
|
2598
2652
|
}
|
|
2653
|
+
module.exports = {
|
|
2654
|
+
isDebug,
|
|
2655
|
+
dlog,
|
|
2656
|
+
warn,
|
|
2657
|
+
writeFileAtomic,
|
|
2658
|
+
applyIfChanged,
|
|
2659
|
+
escapeXml,
|
|
2660
|
+
findFileInTree
|
|
2661
|
+
};
|
|
2662
|
+
}));
|
|
2663
|
+
//#endregion
|
|
2664
|
+
//#region ../expo-modules-link/src/android-paths.cjs
|
|
2665
|
+
var require_android_paths = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
2666
|
+
const path$3 = __require("node:path");
|
|
2667
|
+
const { findFileInTree } = require_common();
|
|
2668
|
+
const ANDROID_DIR = "android";
|
|
2669
|
+
const MAIN_APPLICATION_FILE = "MainApplication.kt";
|
|
2670
|
+
function buildGradlePath(appRoot) {
|
|
2671
|
+
return path$3.join(appRoot, ANDROID_DIR, "app", "build.gradle");
|
|
2672
|
+
}
|
|
2673
|
+
function rootBuildGradlePath(appRoot) {
|
|
2674
|
+
return path$3.join(appRoot, ANDROID_DIR, "build.gradle");
|
|
2675
|
+
}
|
|
2676
|
+
function kspLookupPath(appRoot) {
|
|
2677
|
+
return path$3.join(appRoot, "node_modules", "expo-modules-autolinking", "android", "expo-gradle-plugin", "expo-autolinking-plugin", "src", "main", "kotlin", "expo", "modules", "plugin", "KSPLookup.kt");
|
|
2678
|
+
}
|
|
2679
|
+
function androidManifestPath(appRoot) {
|
|
2680
|
+
return path$3.join(appRoot, ANDROID_DIR, "app", "src", "main", "AndroidManifest.xml");
|
|
2681
|
+
}
|
|
2599
2682
|
function findMainApplicationFile(appRoot) {
|
|
2600
|
-
const javaRoot = path.join(appRoot,
|
|
2601
|
-
|
|
2602
|
-
const stack = [javaRoot];
|
|
2603
|
-
while (stack.length > 0) {
|
|
2604
|
-
const dir = stack.pop();
|
|
2605
|
-
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
2606
|
-
const full = path.join(dir, entry.name);
|
|
2607
|
-
if (entry.isDirectory()) stack.push(full);
|
|
2608
|
-
else if (entry.name === "MainApplication.kt") return full;
|
|
2609
|
-
}
|
|
2610
|
-
}
|
|
2611
|
-
return null;
|
|
2683
|
+
const javaRoot = path$3.join(appRoot, ANDROID_DIR, "app", "src", "main", "java");
|
|
2684
|
+
return findFileInTree(javaRoot, MAIN_APPLICATION_FILE);
|
|
2612
2685
|
}
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2686
|
+
module.exports = {
|
|
2687
|
+
MAIN_APPLICATION_FILE,
|
|
2688
|
+
buildGradlePath,
|
|
2689
|
+
rootBuildGradlePath,
|
|
2690
|
+
kspLookupPath,
|
|
2691
|
+
androidManifestPath,
|
|
2692
|
+
findMainApplicationFile
|
|
2693
|
+
};
|
|
2694
|
+
}));
|
|
2695
|
+
//#endregion
|
|
2696
|
+
//#region ../expo-modules-link/src/android-regions.cjs
|
|
2697
|
+
var require_android_regions = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
2698
|
+
const fs$4 = __require("node:fs");
|
|
2699
|
+
const { dlog, warn, applyIfChanged } = require_common();
|
|
2700
|
+
const { MAIN_APPLICATION_FILE, buildGradlePath, rootBuildGradlePath, kspLookupPath, findMainApplicationFile } = require_android_paths();
|
|
2701
|
+
const BEGIN_TAG = "SYMBIOTE-EXPO-LINK:BEGIN";
|
|
2702
|
+
const END_TAG = "SYMBIOTE-EXPO-LINK:END";
|
|
2703
|
+
const REGION_NOTE = "generated by symbiote-expo-link - do not edit, regenerated on every install";
|
|
2704
|
+
const REACT_ANDROID_ANCHOR = "implementation(\"com.facebook.react:react-android\")";
|
|
2705
|
+
const MODULES_MAP_ANCHOR = "override fun getModulesMap(): Map<Class<out Module>, String?> = mapOf(";
|
|
2706
|
+
const MODULES_PROVIDER_CLASS_ANCHOR = ": ModulesProvider {";
|
|
2707
|
+
const SERVICE_IMPORT = "import expo.modules.kotlin.services.Service";
|
|
2708
|
+
function locateRegion(lines, name, filePath) {
|
|
2709
|
+
const begin = lines.findIndex((line) => line.includes(`${BEGIN_TAG} ${name}`));
|
|
2710
|
+
if (begin === -1) return void 0;
|
|
2711
|
+
const end = lines.findIndex((line, i) => i > begin && line.includes(`${END_TAG} ${name}`));
|
|
2712
|
+
if (end === -1) {
|
|
2713
|
+
warn(`${filePath} has "${BEGIN_TAG} ${name}" but no matching "${END_TAG} ${name}". Restore the end marker (or delete the whole block) and reinstall.`);
|
|
2623
2714
|
return null;
|
|
2624
2715
|
}
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
warn(`no node_modules in ${appRoot}, nothing to link`);
|
|
2630
|
-
return [];
|
|
2631
|
-
}
|
|
2632
|
-
const found = [];
|
|
2633
|
-
for (const entry of fs.readdirSync(modulesRoot, { withFileTypes: true })) {
|
|
2634
|
-
if (entry.name.startsWith(".")) continue;
|
|
2635
|
-
if (entry.name.startsWith("@")) {
|
|
2636
|
-
const scopeDir = path.join(modulesRoot, entry.name);
|
|
2637
|
-
for (const scoped of fs.readdirSync(scopeDir, { withFileTypes: true })) {
|
|
2638
|
-
const entryFound = readManifest(path.join(scopeDir, scoped.name), `${entry.name}/${scoped.name}`);
|
|
2639
|
-
if (entryFound) found.push(entryFound);
|
|
2640
|
-
}
|
|
2641
|
-
continue;
|
|
2642
|
-
}
|
|
2643
|
-
const entryFound = readManifest(path.join(modulesRoot, entry.name), entry.name);
|
|
2644
|
-
if (entryFound) found.push(entryFound);
|
|
2645
|
-
}
|
|
2646
|
-
found.sort((a, b) => a.packageName < b.packageName ? -1 : a.packageName > b.packageName ? 1 : 0);
|
|
2647
|
-
dlog(`found ${found.length} linkable package(s): ${found.map((f) => f.packageName).join(", ")}`);
|
|
2648
|
-
return found;
|
|
2716
|
+
return {
|
|
2717
|
+
begin,
|
|
2718
|
+
end
|
|
2719
|
+
};
|
|
2649
2720
|
}
|
|
2650
2721
|
function writeRegion({ filePath, content, name, indent, bodyLines, findAnchorIndex, anchorLabel }) {
|
|
2651
2722
|
const lines = content.split("\n");
|
|
@@ -2654,14 +2725,10 @@ var import_src = (/* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
2654
2725
|
...bodyLines.map((line) => `${indent}${line}`),
|
|
2655
2726
|
`${indent}// ${END_TAG} ${name}`
|
|
2656
2727
|
];
|
|
2657
|
-
const
|
|
2658
|
-
if (
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
warn(`${filePath} has "${BEGIN_TAG} ${name}" but no matching "${END_TAG} ${name}". Restore the end marker (or delete the whole block) and reinstall.`);
|
|
2662
|
-
return null;
|
|
2663
|
-
}
|
|
2664
|
-
lines.splice(beginIndex, endIndex - beginIndex + 1, ...block);
|
|
2728
|
+
const region = locateRegion(lines, name, filePath);
|
|
2729
|
+
if (region === null) return null;
|
|
2730
|
+
if (region !== void 0) {
|
|
2731
|
+
lines.splice(region.begin, region.end - region.begin + 1, ...block);
|
|
2665
2732
|
return lines.join("\n");
|
|
2666
2733
|
}
|
|
2667
2734
|
const anchorIndex = findAnchorIndex(lines);
|
|
@@ -2672,22 +2739,23 @@ var import_src = (/* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
2672
2739
|
lines.splice(anchorIndex + 1, 0, ...block);
|
|
2673
2740
|
return lines.join("\n");
|
|
2674
2741
|
}
|
|
2675
|
-
function
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2742
|
+
function androidModulesOf(entries) {
|
|
2743
|
+
return entries.flatMap((entry) => entry.manifest.android && Array.isArray(entry.manifest.android.modules) ? entry.manifest.android.modules : []);
|
|
2744
|
+
}
|
|
2745
|
+
function androidServicesOf(entries) {
|
|
2746
|
+
return entries.flatMap((entry) => entry.manifest.android && Array.isArray(entry.manifest.android.services) ? entry.manifest.android.services : []);
|
|
2679
2747
|
}
|
|
2680
2748
|
function patchBuildGradle(appRoot, entries) {
|
|
2681
|
-
const gradlePath =
|
|
2682
|
-
if (!fs.existsSync(gradlePath)) {
|
|
2749
|
+
const gradlePath = buildGradlePath(appRoot);
|
|
2750
|
+
if (!fs$4.existsSync(gradlePath)) {
|
|
2683
2751
|
dlog("android/app/build.gradle not found, skipping");
|
|
2684
2752
|
return;
|
|
2685
2753
|
}
|
|
2686
2754
|
const primaryProjectNames = entries.filter((entry) => entry.manifest.android && entry.manifest.android.gradleProjectName).map((entry) => entry.manifest.android.gradleProjectName);
|
|
2687
2755
|
const serviceProjectNames = androidServicesOf(entries).map((service) => service.gradleProjectName);
|
|
2688
2756
|
const deps = [.../* @__PURE__ */ new Set([...primaryProjectNames, ...serviceProjectNames])].sort().map((name) => `implementation project(':${name}')`);
|
|
2689
|
-
const content = fs.readFileSync(gradlePath, "utf8");
|
|
2690
|
-
|
|
2757
|
+
const content = fs$4.readFileSync(gradlePath, "utf8");
|
|
2758
|
+
const next = writeRegion({
|
|
2691
2759
|
filePath: gradlePath,
|
|
2692
2760
|
content,
|
|
2693
2761
|
name: "DEPENDENCIES",
|
|
@@ -2695,41 +2763,102 @@ var import_src = (/* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
2695
2763
|
bodyLines: deps,
|
|
2696
2764
|
findAnchorIndex: (lines) => lines.findIndex((line) => line.includes(REACT_ANDROID_ANCHOR)),
|
|
2697
2765
|
anchorLabel: `no \`${REACT_ANDROID_ANCHOR}\` line to anchor below`
|
|
2698
|
-
})
|
|
2766
|
+
});
|
|
2767
|
+
applyIfChanged(gradlePath, content, next);
|
|
2699
2768
|
}
|
|
2700
|
-
function
|
|
2701
|
-
|
|
2769
|
+
function removeRegion(content, name, filePath) {
|
|
2770
|
+
const lines = content.split("\n");
|
|
2771
|
+
const region = locateRegion(lines, name, filePath);
|
|
2772
|
+
if (region === null) return null;
|
|
2773
|
+
if (region === void 0) return content;
|
|
2774
|
+
lines.splice(region.begin, region.end - region.begin + 1);
|
|
2775
|
+
return lines.join("\n");
|
|
2702
2776
|
}
|
|
2703
|
-
|
|
2704
|
-
|
|
2777
|
+
const KOTLIN_VERSION_PATTERN = /^\s*kotlinVersion\s*=\s*"([^"]+)"/;
|
|
2778
|
+
const KSP_VERSION_PATTERN = /\bkspVersion\b/;
|
|
2779
|
+
const KSP_PAIR_PATTERN = /"([^"]+)"\s+to\s+"([^"]+)"/g;
|
|
2780
|
+
const LATEST_KSP_PATTERN = /latestKspVersion\s*=\s*"([^"]+)"/;
|
|
2781
|
+
const KSP_UNTIED_FROM_KOTLIN = [2, 3];
|
|
2782
|
+
function isKotlinAtLeast(version, [major, minor]) {
|
|
2783
|
+
const [versionMajor, versionMinor] = version.split(".").map((part) => parseInt(part, 10) || 0);
|
|
2784
|
+
return versionMajor > major || versionMajor === major && versionMinor >= minor;
|
|
2785
|
+
}
|
|
2786
|
+
function resolveKspVersion(appRoot, kotlinVersion, gradlePath) {
|
|
2787
|
+
const lookupPath = kspLookupPath(appRoot);
|
|
2788
|
+
const byHand = `Set kspVersion in ${gradlePath} by hand.`;
|
|
2789
|
+
if (!fs$4.existsSync(lookupPath)) {
|
|
2790
|
+
warn(`cannot read ${lookupPath} to pick the KSP build for Kotlin ${kotlinVersion}. ${byHand}`);
|
|
2791
|
+
return null;
|
|
2792
|
+
}
|
|
2793
|
+
const source = fs$4.readFileSync(lookupPath, "utf8");
|
|
2794
|
+
const table = new Map([...source.matchAll(KSP_PAIR_PATTERN)].map((pair) => [pair[1], pair[2]]));
|
|
2795
|
+
const latest = LATEST_KSP_PATTERN.exec(source);
|
|
2796
|
+
const version = table.get(kotlinVersion) ?? (isKotlinAtLeast(kotlinVersion, KSP_UNTIED_FROM_KOTLIN) && latest ? latest[1] : null);
|
|
2797
|
+
if (version === null) warn(`Kotlin ${kotlinVersion} has no KSP build in Expo's KSPLookup.kt. ${byHand}`);
|
|
2798
|
+
return version;
|
|
2799
|
+
}
|
|
2800
|
+
function patchRootBuildGradle(appRoot, entries) {
|
|
2801
|
+
const gradlePath = rootBuildGradlePath(appRoot);
|
|
2802
|
+
if (!fs$4.existsSync(gradlePath)) {
|
|
2803
|
+
dlog("android/build.gradle not found, skipping");
|
|
2804
|
+
return;
|
|
2805
|
+
}
|
|
2806
|
+
const content = fs$4.readFileSync(gradlePath, "utf8");
|
|
2807
|
+
const isRequired = entries.some((entry) => entry.manifest.android && entry.manifest.android.requiresKsp === true);
|
|
2808
|
+
const withoutRegion = removeRegion(content, "KSP", gradlePath);
|
|
2809
|
+
if (withoutRegion === null) return;
|
|
2810
|
+
if (!isRequired || KSP_VERSION_PATTERN.test(withoutRegion)) {
|
|
2811
|
+
applyIfChanged(gradlePath, content, withoutRegion);
|
|
2812
|
+
return;
|
|
2813
|
+
}
|
|
2814
|
+
const lines = content.split("\n");
|
|
2815
|
+
const anchorIndex = lines.findIndex((line) => KOTLIN_VERSION_PATTERN.test(line));
|
|
2816
|
+
if (anchorIndex === -1) {
|
|
2817
|
+
warn(`cannot create the KSP block in ${gradlePath}: no \`kotlinVersion = "..."\` line to read the Kotlin version from`);
|
|
2818
|
+
return;
|
|
2819
|
+
}
|
|
2820
|
+
const kotlinVersion = KOTLIN_VERSION_PATTERN.exec(lines[anchorIndex])[1];
|
|
2821
|
+
const kspVersion = resolveKspVersion(appRoot, kotlinVersion, gradlePath);
|
|
2822
|
+
if (kspVersion === null) return;
|
|
2823
|
+
const next = writeRegion({
|
|
2824
|
+
filePath: gradlePath,
|
|
2825
|
+
content,
|
|
2826
|
+
name: "KSP",
|
|
2827
|
+
indent: /^\s*/.exec(lines[anchorIndex])[0],
|
|
2828
|
+
bodyLines: [`kspVersion = "${kspVersion}"`],
|
|
2829
|
+
findAnchorIndex: (current) => current.findIndex((line) => KOTLIN_VERSION_PATTERN.test(line)),
|
|
2830
|
+
anchorLabel: "no `kotlinVersion = \"...\"` line to anchor below"
|
|
2831
|
+
});
|
|
2832
|
+
applyIfChanged(gradlePath, content, next);
|
|
2833
|
+
}
|
|
2834
|
+
function lastImportIndex(lines) {
|
|
2835
|
+
return lines.reduce((last, line, i) => line.trim().startsWith("import ") ? i : last, -1);
|
|
2836
|
+
}
|
|
2837
|
+
function mainApplicationImports(modules, services) {
|
|
2838
|
+
return [.../* @__PURE__ */ new Set([
|
|
2839
|
+
...modules.map((module$1) => `import ${module$1.importPath}`),
|
|
2840
|
+
...services.map((service) => `import ${service.importPath}`),
|
|
2841
|
+
SERVICE_IMPORT
|
|
2842
|
+
])].sort();
|
|
2705
2843
|
}
|
|
2706
2844
|
function patchMainApplication(appRoot, entries) {
|
|
2707
2845
|
const filePath = findMainApplicationFile(appRoot);
|
|
2708
2846
|
if (!filePath) {
|
|
2709
|
-
dlog(
|
|
2847
|
+
dlog(`${MAIN_APPLICATION_FILE} not found, skipping`);
|
|
2710
2848
|
return;
|
|
2711
2849
|
}
|
|
2712
2850
|
const modules = androidModulesOf(entries);
|
|
2713
2851
|
const services = androidServicesOf(entries);
|
|
2714
|
-
const imports = [.../* @__PURE__ */ new Set([
|
|
2715
|
-
...modules.map((module$1) => `import ${module$1.importPath}`),
|
|
2716
|
-
...services.map((service) => `import ${service.importPath}`),
|
|
2717
|
-
"import expo.modules.kotlin.services.Service"
|
|
2718
|
-
])].sort();
|
|
2719
2852
|
const mapEntries = [...new Set(modules.map((module$2) => `${module$2.className}::class.java to "${module$2.nativeName}",`))].sort();
|
|
2720
2853
|
const serviceEntries = [...new Set(services.map((service) => ` ${service.className}::class.java,`))].sort();
|
|
2721
|
-
const content = fs.readFileSync(filePath, "utf8");
|
|
2854
|
+
const content = fs$4.readFileSync(filePath, "utf8");
|
|
2722
2855
|
const withImports = writeRegion({
|
|
2723
2856
|
filePath,
|
|
2724
2857
|
content,
|
|
2725
2858
|
name: "IMPORTS",
|
|
2726
2859
|
indent: "",
|
|
2727
|
-
bodyLines:
|
|
2728
|
-
findAnchorIndex:
|
|
2729
|
-
let lastImport = -1;
|
|
2730
|
-
for (let i = 0; i < lines.length; i += 1) if (lines[i].trim().startsWith("import ")) lastImport = i;
|
|
2731
|
-
return lastImport;
|
|
2732
|
-
},
|
|
2860
|
+
bodyLines: mainApplicationImports(modules, services),
|
|
2861
|
+
findAnchorIndex: lastImportIndex,
|
|
2733
2862
|
anchorLabel: "no import statements to anchor below"
|
|
2734
2863
|
});
|
|
2735
2864
|
if (withImports === null) return;
|
|
@@ -2743,7 +2872,7 @@ var import_src = (/* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
2743
2872
|
anchorLabel: "no `getModulesMap()` declaration to anchor below"
|
|
2744
2873
|
});
|
|
2745
2874
|
if (withModules === null) return;
|
|
2746
|
-
|
|
2875
|
+
const next = writeRegion({
|
|
2747
2876
|
filePath,
|
|
2748
2877
|
content: withModules,
|
|
2749
2878
|
name: "SERVICES",
|
|
@@ -2755,31 +2884,45 @@ var import_src = (/* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
2755
2884
|
],
|
|
2756
2885
|
findAnchorIndex: (lines) => lines.findIndex((line) => line.includes(MODULES_PROVIDER_CLASS_ANCHOR)),
|
|
2757
2886
|
anchorLabel: "no `ExpoModulesProvider : ModulesProvider` class declaration to anchor below"
|
|
2758
|
-
})
|
|
2759
|
-
|
|
2760
|
-
function escapeXml(value) {
|
|
2761
|
-
return String(value).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
2887
|
+
});
|
|
2888
|
+
applyIfChanged(filePath, content, next);
|
|
2762
2889
|
}
|
|
2890
|
+
module.exports = {
|
|
2891
|
+
androidServicesOf,
|
|
2892
|
+
patchBuildGradle,
|
|
2893
|
+
patchRootBuildGradle,
|
|
2894
|
+
patchMainApplication
|
|
2895
|
+
};
|
|
2896
|
+
}));
|
|
2897
|
+
//#endregion
|
|
2898
|
+
//#region ../expo-modules-link/src/android-manifest.cjs
|
|
2899
|
+
var require_android_manifest = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
2900
|
+
const fs$3 = __require("node:fs");
|
|
2901
|
+
const { dlog, warn, applyIfChanged, escapeXml } = require_common();
|
|
2902
|
+
const { androidManifestPath } = require_android_paths();
|
|
2903
|
+
const CONFIG_CHANGES_ATTRIBUTE = "android:configChanges";
|
|
2763
2904
|
function escapeXmlAttribute(value) {
|
|
2764
2905
|
return escapeXml(value).replace(/"/g, """);
|
|
2765
2906
|
}
|
|
2766
|
-
function
|
|
2767
|
-
const match = new RegExp(`<${tagName}(?=[\\s>/])`).exec(content);
|
|
2768
|
-
if (!match) return null;
|
|
2907
|
+
function scanTagEnd(content, from) {
|
|
2769
2908
|
let quote = null;
|
|
2770
|
-
for (let i =
|
|
2909
|
+
for (let i = from; i < content.length; i += 1) {
|
|
2771
2910
|
const char = content[i];
|
|
2772
2911
|
if (quote !== null) {
|
|
2773
2912
|
if (char === quote) quote = null;
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
if (char === "\"" || char === "'") quote = char;
|
|
2777
|
-
else if (char === ">") return {
|
|
2778
|
-
start: match.index,
|
|
2779
|
-
end: i
|
|
2780
|
-
};
|
|
2913
|
+
} else if (char === "\"" || char === "'") quote = char;
|
|
2914
|
+
else if (char === ">") return i;
|
|
2781
2915
|
}
|
|
2782
|
-
return
|
|
2916
|
+
return -1;
|
|
2917
|
+
}
|
|
2918
|
+
function findTag(content, tagName) {
|
|
2919
|
+
const match = new RegExp(`<${tagName}(?=[\\s>/])`).exec(content);
|
|
2920
|
+
if (!match) return null;
|
|
2921
|
+
const end = scanTagEnd(content, match.index);
|
|
2922
|
+
return end === -1 ? null : {
|
|
2923
|
+
start: match.index,
|
|
2924
|
+
end
|
|
2925
|
+
};
|
|
2783
2926
|
}
|
|
2784
2927
|
function findApplicationTag(content) {
|
|
2785
2928
|
return findTag(content, "application");
|
|
@@ -2789,27 +2932,22 @@ var import_src = (/* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
2789
2932
|
const re = new RegExp(`<${tagName}(?=[\\s>/])`, "g");
|
|
2790
2933
|
let match = re.exec(content);
|
|
2791
2934
|
while (match) {
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
if (char === "\"" || char === "'") quote = char;
|
|
2800
|
-
else if (char === ">") {
|
|
2801
|
-
tags.push({
|
|
2802
|
-
start: match.index,
|
|
2803
|
-
end: i
|
|
2804
|
-
});
|
|
2805
|
-
re.lastIndex = i + 1;
|
|
2806
|
-
break;
|
|
2807
|
-
}
|
|
2935
|
+
const end = scanTagEnd(content, match.index);
|
|
2936
|
+
if (end !== -1) {
|
|
2937
|
+
tags.push({
|
|
2938
|
+
start: match.index,
|
|
2939
|
+
end
|
|
2940
|
+
});
|
|
2941
|
+
re.lastIndex = end + 1;
|
|
2808
2942
|
}
|
|
2809
2943
|
match = re.exec(content);
|
|
2810
2944
|
}
|
|
2811
2945
|
return tags;
|
|
2812
2946
|
}
|
|
2947
|
+
function readXmlAttribute(tagText, name) {
|
|
2948
|
+
const match = new RegExp(`\\s${name}\\s*=\\s*"([^"]*)"`).exec(tagText);
|
|
2949
|
+
return match ? match[1] : null;
|
|
2950
|
+
}
|
|
2813
2951
|
function findMainActivityTag(content) {
|
|
2814
2952
|
for (const tag of findAllTags(content, "activity")) {
|
|
2815
2953
|
const name = readXmlAttribute(content.slice(tag.start, tag.end), "android:name");
|
|
@@ -2817,26 +2955,48 @@ var import_src = (/* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
2817
2955
|
}
|
|
2818
2956
|
return null;
|
|
2819
2957
|
}
|
|
2820
|
-
function
|
|
2821
|
-
const
|
|
2822
|
-
return
|
|
2958
|
+
function attributeSeparator(tagText) {
|
|
2959
|
+
const lastNewline = tagText.lastIndexOf("\n");
|
|
2960
|
+
return lastNewline === -1 ? " " : `\n${/^[ \t]*/.exec(tagText.slice(lastNewline + 1))[0]}`;
|
|
2823
2961
|
}
|
|
2824
|
-
function
|
|
2962
|
+
function appendAttribute(content, tag, name, escapedValue) {
|
|
2963
|
+
const separator = attributeSeparator(content.slice(tag.start, tag.end));
|
|
2964
|
+
const insertAt = content[tag.end - 1] === "/" ? tag.end - 1 : tag.end;
|
|
2965
|
+
return `${content.slice(0, insertAt)}${separator}${name}="${escapedValue}"${content.slice(insertAt)}`;
|
|
2966
|
+
}
|
|
2967
|
+
function androidListOf(entries, field) {
|
|
2968
|
+
return entries.flatMap((entry) => entry.manifest.android && entry.manifest.android[field] || []);
|
|
2969
|
+
}
|
|
2970
|
+
function loadAndroidManifest(appRoot, skipLabel) {
|
|
2971
|
+
const manifestPath = androidManifestPath(appRoot);
|
|
2972
|
+
if (!fs$3.existsSync(manifestPath)) {
|
|
2973
|
+
dlog(`AndroidManifest.xml not found, skipping ${skipLabel}`);
|
|
2974
|
+
return null;
|
|
2975
|
+
}
|
|
2976
|
+
return {
|
|
2977
|
+
manifestPath,
|
|
2978
|
+
content: fs$3.readFileSync(manifestPath, "utf8")
|
|
2979
|
+
};
|
|
2980
|
+
}
|
|
2981
|
+
function wantedApplicationAttributes(entries) {
|
|
2825
2982
|
const wanted = /* @__PURE__ */ new Map();
|
|
2826
2983
|
for (const entry of entries) {
|
|
2827
2984
|
const attributes = entry.manifest.android && entry.manifest.android.manifestApplicationAttributes;
|
|
2828
|
-
if (!
|
|
2829
|
-
for (const [name, value] of Object.entries(attributes)) if (!wanted.has(name)) wanted.set(name, value);
|
|
2985
|
+
for (const [name, value] of Object.entries(attributes || {})) if (!wanted.has(name)) wanted.set(name, value);
|
|
2830
2986
|
}
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
const
|
|
2987
|
+
return [...wanted].sort((a, b) => a[0] < b[0] ? -1 : 1);
|
|
2988
|
+
}
|
|
2989
|
+
function warnOnAttributeDrift(manifestPath, name, existing, value) {
|
|
2990
|
+
warn(`${name} in ${manifestPath} reads "${existing}" but the package manifest says "${value}". Keeping the file as-is; edit it by hand if you want the manifest's.`);
|
|
2991
|
+
}
|
|
2992
|
+
function patchAndroidManifest(appRoot, entries) {
|
|
2993
|
+
const wanted = wantedApplicationAttributes(entries);
|
|
2994
|
+
if (wanted.length === 0) return;
|
|
2995
|
+
const loaded = loadAndroidManifest(appRoot, "application attributes");
|
|
2996
|
+
if (!loaded) return;
|
|
2997
|
+
const { manifestPath, content } = loaded;
|
|
2838
2998
|
let next = content;
|
|
2839
|
-
for (const [name, value] of
|
|
2999
|
+
for (const [name, value] of wanted) {
|
|
2840
3000
|
if (!/^[\w.:-]+$/.test(name)) {
|
|
2841
3001
|
warn(`skipping the manifest attribute "${name}": not a plain XML attribute name`);
|
|
2842
3002
|
continue;
|
|
@@ -2846,79 +3006,55 @@ var import_src = (/* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
2846
3006
|
warn(`${manifestPath} has no <application> element, skipping ${name}`);
|
|
2847
3007
|
return;
|
|
2848
3008
|
}
|
|
2849
|
-
const tagText = next.slice(tag.start, tag.end);
|
|
2850
3009
|
const escaped = escapeXmlAttribute(value);
|
|
2851
|
-
const existing = readXmlAttribute(
|
|
2852
|
-
if (existing
|
|
2853
|
-
|
|
2854
|
-
continue;
|
|
2855
|
-
}
|
|
2856
|
-
const lastNewline = tagText.lastIndexOf("\n");
|
|
2857
|
-
const separator = lastNewline === -1 ? " " : `\n${/^[ \t]*/.exec(tagText.slice(lastNewline + 1))[0]}`;
|
|
2858
|
-
const insertAt = next[tag.end - 1] === "/" ? tag.end - 1 : tag.end;
|
|
2859
|
-
next = `${next.slice(0, insertAt)}${separator}${name}="${escaped}"${next.slice(insertAt)}`;
|
|
3010
|
+
const existing = readXmlAttribute(next.slice(tag.start, tag.end), name);
|
|
3011
|
+
if (existing === null) next = appendAttribute(next, tag, name, escaped);
|
|
3012
|
+
else if (existing !== escaped) warnOnAttributeDrift(manifestPath, name, existing, value);
|
|
2860
3013
|
}
|
|
2861
3014
|
applyIfChanged(manifestPath, content, next);
|
|
2862
3015
|
}
|
|
3016
|
+
function replaceConfigChangesValue(content, tag, merged) {
|
|
3017
|
+
const tagText = content.slice(tag.start, tag.end);
|
|
3018
|
+
const valueMatch = new RegExp(`${CONFIG_CHANGES_ATTRIBUTE}\\s*=\\s*"`).exec(tagText);
|
|
3019
|
+
const valueStart = valueMatch.index + valueMatch[0].length;
|
|
3020
|
+
const valueEnd = tagText.indexOf("\"", valueStart);
|
|
3021
|
+
const nextTagText = tagText.slice(0, valueStart) + merged + tagText.slice(valueEnd);
|
|
3022
|
+
return content.slice(0, tag.start) + nextTagText + content.slice(tag.end);
|
|
3023
|
+
}
|
|
3024
|
+
function addConfigChangesAttribute(content, tag, merged) {
|
|
3025
|
+
const selfClosing = content[tag.end - 1] === "/";
|
|
3026
|
+
const insertAt = selfClosing ? tag.end - 1 : tag.end;
|
|
3027
|
+
const separator = attributeSeparator(content.slice(tag.start, tag.end));
|
|
3028
|
+
return `${content.slice(0, insertAt).replace(/[ \t]+$/, "")}${separator}${CONFIG_CHANGES_ATTRIBUTE}="${merged}"${selfClosing ? " " : ""}${content.slice(insertAt)}`;
|
|
3029
|
+
}
|
|
2863
3030
|
function patchMainActivityConfigChanges(appRoot, entries) {
|
|
2864
|
-
const wanted =
|
|
2865
|
-
for (const entry of entries) {
|
|
2866
|
-
const values = entry.manifest.android && entry.manifest.android.mainActivityConfigChanges;
|
|
2867
|
-
if (!values) continue;
|
|
2868
|
-
for (const value of values) wanted.add(value);
|
|
2869
|
-
}
|
|
3031
|
+
const wanted = new Set(androidListOf(entries, "mainActivityConfigChanges"));
|
|
2870
3032
|
if (wanted.size === 0) return;
|
|
2871
|
-
const
|
|
2872
|
-
if (!
|
|
2873
|
-
|
|
2874
|
-
return;
|
|
2875
|
-
}
|
|
2876
|
-
const content = fs.readFileSync(manifestPath, "utf8");
|
|
3033
|
+
const loaded = loadAndroidManifest(appRoot, "MainActivity configChanges");
|
|
3034
|
+
if (!loaded) return;
|
|
3035
|
+
const { manifestPath, content } = loaded;
|
|
2877
3036
|
const tag = findMainActivityTag(content);
|
|
2878
3037
|
if (!tag) {
|
|
2879
3038
|
warn(`${manifestPath} has no MainActivity <activity> element, skipping configChanges`);
|
|
2880
3039
|
return;
|
|
2881
3040
|
}
|
|
2882
|
-
const
|
|
2883
|
-
const existingValue = readXmlAttribute(tagText, "android:configChanges");
|
|
3041
|
+
const existingValue = readXmlAttribute(content.slice(tag.start, tag.end), CONFIG_CHANGES_ATTRIBUTE);
|
|
2884
3042
|
const existingTokens = existingValue ? existingValue.split("|").map((token) => token.trim()).filter(Boolean) : [];
|
|
2885
|
-
const
|
|
2886
|
-
const missing = [...wanted].filter((token) => !existingSet.has(token)).sort();
|
|
3043
|
+
const missing = [...wanted].filter((token) => !existingTokens.includes(token)).sort();
|
|
2887
3044
|
if (missing.length === 0) return;
|
|
2888
3045
|
const merged = [...existingTokens, ...missing].join("|");
|
|
2889
|
-
|
|
2890
|
-
if (existingValue !== null) {
|
|
2891
|
-
const valueMatch = /android:configChanges\s*=\s*"/.exec(tagText);
|
|
2892
|
-
const valueStart = valueMatch.index + valueMatch[0].length;
|
|
2893
|
-
const valueEnd = tagText.indexOf("\"", valueStart);
|
|
2894
|
-
const nextTagText = tagText.slice(0, valueStart) + merged + tagText.slice(valueEnd);
|
|
2895
|
-
next = content.slice(0, tag.start) + nextTagText + content.slice(tag.end);
|
|
2896
|
-
} else {
|
|
2897
|
-
const lastNewline = tagText.lastIndexOf("\n");
|
|
2898
|
-
const separator = lastNewline === -1 ? " " : `\n${/^[ \t]*/.exec(tagText.slice(lastNewline + 1))[0]}`;
|
|
2899
|
-
const selfClosing = content[tag.end - 1] === "/";
|
|
2900
|
-
const insertAt = selfClosing ? tag.end - 1 : tag.end;
|
|
2901
|
-
next = `${content.slice(0, insertAt).replace(/[ \t]+$/, "")}${separator}android:configChanges="${merged}"${selfClosing ? " " : ""}${content.slice(insertAt)}`;
|
|
2902
|
-
}
|
|
3046
|
+
const next = existingValue !== null ? replaceConfigChangesValue(content, tag, merged) : addConfigChangesAttribute(content, tag, merged);
|
|
2903
3047
|
applyIfChanged(manifestPath, content, next);
|
|
2904
3048
|
}
|
|
2905
3049
|
function readManifestPermissions(content) {
|
|
2906
3050
|
return new Set([...content.matchAll(/<uses-permission\s[^>]*android:name="([^"]+)"/g)].map((match) => match[1]));
|
|
2907
3051
|
}
|
|
2908
3052
|
function patchAndroidManifestPermissions(appRoot, entries) {
|
|
2909
|
-
const wanted =
|
|
2910
|
-
for (const entry of entries) {
|
|
2911
|
-
const permissions = entry.manifest.android && entry.manifest.android.manifestPermissions;
|
|
2912
|
-
if (!permissions) continue;
|
|
2913
|
-
for (const permission of permissions) wanted.add(permission);
|
|
2914
|
-
}
|
|
3053
|
+
const wanted = new Set(androidListOf(entries, "manifestPermissions"));
|
|
2915
3054
|
if (wanted.size === 0) return;
|
|
2916
|
-
const
|
|
2917
|
-
if (!
|
|
2918
|
-
|
|
2919
|
-
return;
|
|
2920
|
-
}
|
|
2921
|
-
const content = fs.readFileSync(manifestPath, "utf8");
|
|
3055
|
+
const loaded = loadAndroidManifest(appRoot, "permissions");
|
|
3056
|
+
if (!loaded) return;
|
|
3057
|
+
const { manifestPath, content } = loaded;
|
|
2922
3058
|
const existing = readManifestPermissions(content);
|
|
2923
3059
|
const missing = [...wanted].filter((permission) => !existing.has(permission)).sort();
|
|
2924
3060
|
if (missing.length === 0) return;
|
|
@@ -2941,21 +3077,13 @@ var import_src = (/* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
2941
3077
|
return `${openTag}>\n${indent} <intent-filter>\n${service.intentFilterActions.map((action) => `${indent} <action android:name="${escapeXmlAttribute(action)}" />`).join("\n")}\n${indent} </intent-filter>\n${indent}</service>`;
|
|
2942
3078
|
}
|
|
2943
3079
|
function patchAndroidManifestServices(appRoot, entries) {
|
|
2944
|
-
const
|
|
2945
|
-
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
}
|
|
2950
|
-
if (wanted.size === 0) return;
|
|
2951
|
-
const manifestPath = path.join(appRoot, "android", "app", "src", "main", "AndroidManifest.xml");
|
|
2952
|
-
if (!fs.existsSync(manifestPath)) {
|
|
2953
|
-
dlog("AndroidManifest.xml not found, skipping services");
|
|
2954
|
-
return;
|
|
2955
|
-
}
|
|
2956
|
-
const content = fs.readFileSync(manifestPath, "utf8");
|
|
3080
|
+
const declared = androidListOf(entries, "manifestServices");
|
|
3081
|
+
if (declared.length === 0) return;
|
|
3082
|
+
const loaded = loadAndroidManifest(appRoot, "services");
|
|
3083
|
+
if (!loaded) return;
|
|
3084
|
+
const { manifestPath, content } = loaded;
|
|
2957
3085
|
const existing = readManifestServiceNames(content);
|
|
2958
|
-
const missing = [...
|
|
3086
|
+
const missing = [...new Map(declared.map((service) => [service.name, service])).values()].filter((service) => !existing.has(service.name)).sort((a, b) => a.name < b.name ? -1 : 1);
|
|
2959
3087
|
if (missing.length === 0) return;
|
|
2960
3088
|
const tag = findApplicationTag(content);
|
|
2961
3089
|
if (!tag) {
|
|
@@ -2967,50 +3095,130 @@ var import_src = (/* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
2967
3095
|
return;
|
|
2968
3096
|
}
|
|
2969
3097
|
const lastNewline = content.lastIndexOf("\n", tag.start);
|
|
2970
|
-
const
|
|
2971
|
-
const blocks = missing.map((service) => buildServiceXml(service,
|
|
3098
|
+
const tagIndent = /^[ \t]*/.exec(content.slice(lastNewline + 1, tag.start))[0];
|
|
3099
|
+
const blocks = missing.map((service) => buildServiceXml(service, `${tagIndent} `)).join("\n");
|
|
2972
3100
|
const insertAt = tag.end + 1;
|
|
2973
3101
|
applyIfChanged(manifestPath, content, `${content.slice(0, insertAt)}\n${blocks}${content.slice(insertAt)}`);
|
|
2974
3102
|
}
|
|
3103
|
+
module.exports = {
|
|
3104
|
+
patchAndroidManifest,
|
|
3105
|
+
patchAndroidManifestPermissions,
|
|
3106
|
+
patchAndroidManifestServices,
|
|
3107
|
+
patchMainActivityConfigChanges
|
|
3108
|
+
};
|
|
3109
|
+
}));
|
|
3110
|
+
//#endregion
|
|
3111
|
+
//#region ../expo-modules-link/src/ios-xcode-project.cjs
|
|
3112
|
+
var require_ios_xcode_project = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
3113
|
+
const crypto = __require("node:crypto");
|
|
3114
|
+
const fs$2 = __require("node:fs");
|
|
3115
|
+
const path$2 = __require("node:path");
|
|
3116
|
+
const { dlog, warn, writeFileAtomic } = require_common();
|
|
3117
|
+
const XCODEPROJ_SUFFIX = ".xcodeproj";
|
|
3118
|
+
const PODS_PROJECT = "Pods.xcodeproj";
|
|
3119
|
+
const PBXPROJ = "project.pbxproj";
|
|
3120
|
+
const OBJECT_ID_LENGTH = 24;
|
|
3121
|
+
const EMPTY_ENTITLEMENTS = `<?xml version="1.0" encoding="UTF-8"?>
|
|
3122
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
3123
|
+
<plist version="1.0">
|
|
3124
|
+
<dict>
|
|
3125
|
+
</dict>
|
|
3126
|
+
</plist>
|
|
3127
|
+
`;
|
|
3128
|
+
function escapeRegExp(text) {
|
|
3129
|
+
return text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
3130
|
+
}
|
|
3131
|
+
function pbxLabel(name) {
|
|
3132
|
+
return [
|
|
3133
|
+
"/*",
|
|
3134
|
+
name,
|
|
3135
|
+
"*/"
|
|
3136
|
+
].join(" ");
|
|
3137
|
+
}
|
|
3138
|
+
function projectRelative(iosDir, filePath) {
|
|
3139
|
+
return path$2.relative(iosDir, filePath).split(path$2.sep).join("/");
|
|
3140
|
+
}
|
|
3141
|
+
function findAppPbxproj(iosDir) {
|
|
3142
|
+
if (!fs$2.existsSync(iosDir)) return null;
|
|
3143
|
+
const project = fs$2.readdirSync(iosDir).find((name) => name.endsWith(XCODEPROJ_SUFFIX) && name !== PODS_PROJECT);
|
|
3144
|
+
return project ? path$2.join(iosDir, project, PBXPROJ) : null;
|
|
3145
|
+
}
|
|
3146
|
+
function newObjectId(content, seed) {
|
|
3147
|
+
for (let salt = 0;; salt += 1) {
|
|
3148
|
+
const id = crypto.createHash("sha1").update(`${seed}:${salt}`).digest("hex").slice(0, OBJECT_ID_LENGTH).toUpperCase();
|
|
3149
|
+
if (!content.includes(id)) return id;
|
|
3150
|
+
}
|
|
3151
|
+
}
|
|
3152
|
+
function withSigningSetting(content, plistRef, entitlementsRef) {
|
|
3153
|
+
const settings = new RegExp(`^(\\t+)INFOPLIST_FILE = "?${escapeRegExp(plistRef)}"?;\\n`, "gm");
|
|
3154
|
+
if (!settings.test(content)) return null;
|
|
3155
|
+
return content.replace(settings, (line, indent, offset) => {
|
|
3156
|
+
return content.slice(content.lastIndexOf("buildSettings", offset), offset).includes("CODE_SIGN_ENTITLEMENTS") ? line : `${indent}CODE_SIGN_ENTITLEMENTS = ${entitlementsRef};\n${line}`;
|
|
3157
|
+
});
|
|
3158
|
+
}
|
|
3159
|
+
function withFileReference(content, plistRef, entitlementsRef) {
|
|
3160
|
+
const plist = new RegExp(`^(\\t+)(\\w+) /\\* ([^*]+) \\*/ = \\{isa = PBXFileReference;[^\\n]*path = "?${escapeRegExp(plistRef)}"?;[^\\n]*\\n`, "m").exec(content);
|
|
3161
|
+
if (!plist) return null;
|
|
3162
|
+
const [line, indent, plistId, plistName] = plist;
|
|
3163
|
+
const fileName = path$2.posix.basename(entitlementsRef);
|
|
3164
|
+
const label = pbxLabel(fileName);
|
|
3165
|
+
const id = newObjectId(content, entitlementsRef);
|
|
3166
|
+
const attributes = `isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; name = ${fileName}; path = ${entitlementsRef}; sourceTree = "<group>";`;
|
|
3167
|
+
const withReference = content.replace(line, () => `${line}${indent}${id} ${label} = {${attributes} };\n`);
|
|
3168
|
+
const inGroup = new RegExp(`^(\\t+)${plistId} ${escapeRegExp(pbxLabel(plistName))},\\n`, "m").exec(withReference);
|
|
3169
|
+
if (!inGroup) return null;
|
|
3170
|
+
return withReference.replace(inGroup[0], () => `${inGroup[0]}${inGroup[1]}${id} ${label},\n`);
|
|
3171
|
+
}
|
|
3172
|
+
function addEntitlementsToProject(content, plistRef, entitlementsRef) {
|
|
3173
|
+
const withSigning = withSigningSetting(content, plistRef, entitlementsRef);
|
|
3174
|
+
return withSigning === null ? null : withFileReference(withSigning, plistRef, entitlementsRef);
|
|
3175
|
+
}
|
|
3176
|
+
function createEntitlementsFile(appRoot, plistPath) {
|
|
3177
|
+
const iosDir = path$2.join(appRoot, "ios");
|
|
3178
|
+
const pbxprojPath = findAppPbxproj(iosDir);
|
|
3179
|
+
if (!pbxprojPath) {
|
|
3180
|
+
dlog("no Xcode project found, not creating an .entitlements file");
|
|
3181
|
+
return null;
|
|
3182
|
+
}
|
|
3183
|
+
const appName = path$2.basename(path$2.dirname(plistPath));
|
|
3184
|
+
const filePath = path$2.join(path$2.dirname(plistPath), `${appName}.entitlements`);
|
|
3185
|
+
const next = addEntitlementsToProject(fs$2.readFileSync(pbxprojPath, "utf8"), projectRelative(iosDir, plistPath), projectRelative(iosDir, filePath));
|
|
3186
|
+
if (next === null) {
|
|
3187
|
+
warn(`could not wire an .entitlements file into ${pbxprojPath}, add one and set CODE_SIGN_ENTITLEMENTS by hand`);
|
|
3188
|
+
return null;
|
|
3189
|
+
}
|
|
3190
|
+
writeFileAtomic(pbxprojPath, next);
|
|
3191
|
+
writeFileAtomic(filePath, EMPTY_ENTITLEMENTS);
|
|
3192
|
+
dlog(`created ${filePath} and wired it into ${pbxprojPath}`);
|
|
3193
|
+
return filePath;
|
|
3194
|
+
}
|
|
3195
|
+
module.exports = { createEntitlementsFile };
|
|
3196
|
+
}));
|
|
3197
|
+
//#endregion
|
|
3198
|
+
//#region ../expo-modules-link/src/ios-plist.cjs
|
|
3199
|
+
var require_ios_plist = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
3200
|
+
const fs$1 = __require("node:fs");
|
|
3201
|
+
const path$1 = __require("node:path");
|
|
3202
|
+
const { dlog, warn, applyIfChanged, escapeXml, findFileInTree } = require_common();
|
|
3203
|
+
const { createEntitlementsFile } = require_ios_xcode_project();
|
|
3204
|
+
const INFO_PLIST = "Info.plist";
|
|
3205
|
+
const NON_APP_DIRS = /Pods|build|DerivedData|Tests/i;
|
|
3206
|
+
function findInfoPlistFile(appRoot) {
|
|
3207
|
+
return findFileInTree(path$1.join(appRoot, "ios"), INFO_PLIST, NON_APP_DIRS);
|
|
3208
|
+
}
|
|
3209
|
+
function findEntitlementsFile(appRoot) {
|
|
3210
|
+
const plistPath = findInfoPlistFile(appRoot);
|
|
3211
|
+
if (!plistPath) return null;
|
|
3212
|
+
const dir = path$1.dirname(plistPath);
|
|
3213
|
+
const name = fs$1.readdirSync(dir).find((file) => file.endsWith(".entitlements"));
|
|
3214
|
+
return name ? path$1.join(dir, name) : null;
|
|
3215
|
+
}
|
|
2975
3216
|
function readPlistString(content, key) {
|
|
2976
3217
|
const keyIndex = content.indexOf(`<key>${key}</key>`);
|
|
2977
3218
|
if (keyIndex === -1) return null;
|
|
2978
3219
|
const match = /<string>([\s\S]*?)<\/string>/.exec(content.slice(keyIndex));
|
|
2979
3220
|
return match ? match[1] : null;
|
|
2980
3221
|
}
|
|
2981
|
-
function patchInfoPlist(appRoot, entries) {
|
|
2982
|
-
const wanted = /* @__PURE__ */ new Map();
|
|
2983
|
-
for (const entry of entries) {
|
|
2984
|
-
const keys = entry.manifest.ios && entry.manifest.ios.infoPlistKeys;
|
|
2985
|
-
if (!keys) continue;
|
|
2986
|
-
for (const [key, description] of Object.entries(keys)) if (!wanted.has(key)) wanted.set(key, description);
|
|
2987
|
-
}
|
|
2988
|
-
if (wanted.size === 0) return;
|
|
2989
|
-
const plistPath = findInfoPlistFile(appRoot);
|
|
2990
|
-
if (!plistPath) {
|
|
2991
|
-
dlog("Info.plist not found, skipping iOS permission strings");
|
|
2992
|
-
return;
|
|
2993
|
-
}
|
|
2994
|
-
const content = fs.readFileSync(plistPath, "utf8");
|
|
2995
|
-
let next = content;
|
|
2996
|
-
for (const [key, description] of [...wanted].sort((a, b) => a[0] < b[0] ? -1 : 1)) {
|
|
2997
|
-
const escaped = escapeXml(description);
|
|
2998
|
-
const existing = readPlistString(next, key);
|
|
2999
|
-
if (existing !== null) {
|
|
3000
|
-
if (existing !== escaped) warn(`${key} in ${plistPath} reads "${existing}" but the package manifest says "${description}". Keeping the file as-is; edit it by hand if you want the manifest's.`);
|
|
3001
|
-
continue;
|
|
3002
|
-
}
|
|
3003
|
-
const plistCloseIndex = next.lastIndexOf("</plist>");
|
|
3004
|
-
const dictCloseIndex = next.lastIndexOf("</dict>", plistCloseIndex === -1 ? void 0 : plistCloseIndex);
|
|
3005
|
-
if (dictCloseIndex === -1) {
|
|
3006
|
-
warn(`${plistPath} has no closing </dict>, skipping ${key}`);
|
|
3007
|
-
continue;
|
|
3008
|
-
}
|
|
3009
|
-
const block = `\t<key>${key}</key>\n\t<string>${escaped}</string>\n`;
|
|
3010
|
-
next = next.slice(0, dictCloseIndex) + block + next.slice(dictCloseIndex);
|
|
3011
|
-
}
|
|
3012
|
-
applyIfChanged(plistPath, content, next);
|
|
3013
|
-
}
|
|
3014
3222
|
function readPlistArray(content, key) {
|
|
3015
3223
|
const keyTag = `<key>${key}</key>`;
|
|
3016
3224
|
const keyIndex = content.indexOf(keyTag);
|
|
@@ -3023,44 +3231,151 @@ var import_src = (/* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
3023
3231
|
items: [...content.slice(arrayOpenIndex + 7, arrayCloseIndex).matchAll(/<string>([\s\S]*?)<\/string>/g)].map((match) => match[1])
|
|
3024
3232
|
};
|
|
3025
3233
|
}
|
|
3026
|
-
function
|
|
3234
|
+
function insertPlistKey(source, plistPath, key, valueXml) {
|
|
3235
|
+
const content = source.replace(/<dict\/>(\s*<\/plist>)/, (_, tail) => `<dict>\n</dict>${tail}`);
|
|
3236
|
+
const plistCloseIndex = content.lastIndexOf("</plist>");
|
|
3237
|
+
const dictCloseIndex = content.lastIndexOf("</dict>", plistCloseIndex === -1 ? void 0 : plistCloseIndex);
|
|
3238
|
+
if (dictCloseIndex === -1) {
|
|
3239
|
+
warn(`${plistPath} has no closing </dict>, skipping ${key}`);
|
|
3240
|
+
return content;
|
|
3241
|
+
}
|
|
3242
|
+
const block = `\t<key>${key}</key>\n${valueXml}\n`;
|
|
3243
|
+
return content.slice(0, dictCloseIndex) + block + content.slice(dictCloseIndex);
|
|
3244
|
+
}
|
|
3245
|
+
function mergePlistArray(content, plistPath, key, items) {
|
|
3246
|
+
const existing = readPlistArray(content, key);
|
|
3247
|
+
if (existing === null) return insertPlistKey(content, plistPath, key, `\t<array>\n${items.map((item) => `\t\t<string>${escapeXml(item)}</string>`).join("\n")}\n\t</array>`);
|
|
3248
|
+
const missing = items.filter((item) => !existing.items.includes(item));
|
|
3249
|
+
if (missing.length === 0) return content;
|
|
3250
|
+
const missingLines = missing.map((item) => `\t\t<string>${escapeXml(item)}</string>\n`).join("");
|
|
3251
|
+
return content.slice(0, existing.arrayCloseIndex) + missingLines + content.slice(existing.arrayCloseIndex);
|
|
3252
|
+
}
|
|
3253
|
+
function scalarXml(value) {
|
|
3254
|
+
return typeof value === "boolean" ? `\t<${value}/>` : `\t<string>${escapeXml(value)}</string>`;
|
|
3255
|
+
}
|
|
3256
|
+
function warnOnStringDrift(content, plistPath, key, value) {
|
|
3257
|
+
const existing = readPlistString(content, key);
|
|
3258
|
+
if (typeof value !== "string" || existing === null || existing === escapeXml(value)) return;
|
|
3259
|
+
warn(`${key} in ${plistPath} reads "${existing}" but the package manifest says "${value}". Keeping the file as-is; edit it by hand if you want the manifest's.`);
|
|
3260
|
+
}
|
|
3261
|
+
function applyKey(content, plistPath, key, value) {
|
|
3262
|
+
if (value instanceof Set) return mergePlistArray(content, plistPath, key, [...value]);
|
|
3263
|
+
if (content.includes(`<key>${key}</key>`)) {
|
|
3264
|
+
warnOnStringDrift(content, plistPath, key, value);
|
|
3265
|
+
return content;
|
|
3266
|
+
}
|
|
3267
|
+
return insertPlistKey(content, plistPath, key, scalarXml(value));
|
|
3268
|
+
}
|
|
3269
|
+
function addWanted(wanted, key, value) {
|
|
3270
|
+
if (Array.isArray(value)) {
|
|
3271
|
+
wanted.set(key, /* @__PURE__ */ new Set([...wanted.get(key) || [], ...value]));
|
|
3272
|
+
return;
|
|
3273
|
+
}
|
|
3274
|
+
if (!wanted.has(key)) wanted.set(key, value);
|
|
3275
|
+
}
|
|
3276
|
+
function wantedKeys(entries, field) {
|
|
3027
3277
|
const wanted = /* @__PURE__ */ new Map();
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
|
|
3278
|
+
const declaredPerPackage = entries.map((entry) => entry.manifest.ios && entry.manifest.ios[field]);
|
|
3279
|
+
for (const declared of declaredPerPackage) for (const [key, value] of Object.entries(declared || {})) addWanted(wanted, key, value);
|
|
3280
|
+
return [...wanted].sort((a, b) => a[0] < b[0] ? -1 : 1);
|
|
3281
|
+
}
|
|
3282
|
+
function patchPlistFile(filePath, wanted) {
|
|
3283
|
+
const content = fs$1.readFileSync(filePath, "utf8");
|
|
3284
|
+
const next = wanted.reduce((acc, [key, value]) => applyKey(acc, filePath, key, value), content);
|
|
3285
|
+
applyIfChanged(filePath, content, next);
|
|
3286
|
+
}
|
|
3287
|
+
function patchInfoPlistField(appRoot, entries, field) {
|
|
3288
|
+
const wanted = wantedKeys(entries, field);
|
|
3289
|
+
if (wanted.length === 0) return;
|
|
3290
|
+
const plistPath = findInfoPlistFile(appRoot);
|
|
3291
|
+
if (!plistPath) {
|
|
3292
|
+
dlog(`${INFO_PLIST} not found, skipping iOS ${field}`);
|
|
3293
|
+
return;
|
|
3035
3294
|
}
|
|
3036
|
-
|
|
3295
|
+
patchPlistFile(plistPath, wanted);
|
|
3296
|
+
}
|
|
3297
|
+
function patchInfoPlist(appRoot, entries) {
|
|
3298
|
+
patchInfoPlistField(appRoot, entries, "infoPlistKeys");
|
|
3299
|
+
}
|
|
3300
|
+
function patchInfoPlistArrays(appRoot, entries) {
|
|
3301
|
+
patchInfoPlistField(appRoot, entries, "infoPlistArrayKeys");
|
|
3302
|
+
}
|
|
3303
|
+
function patchInfoPlistBooleans(appRoot, entries) {
|
|
3304
|
+
patchInfoPlistField(appRoot, entries, "infoPlistBooleanKeys");
|
|
3305
|
+
}
|
|
3306
|
+
function patchEntitlements(appRoot, entries) {
|
|
3307
|
+
const wanted = wantedKeys(entries, "entitlements");
|
|
3308
|
+
if (wanted.length === 0) return;
|
|
3037
3309
|
const plistPath = findInfoPlistFile(appRoot);
|
|
3038
3310
|
if (!plistPath) {
|
|
3039
|
-
dlog(
|
|
3311
|
+
dlog(`${INFO_PLIST} not found, skipping iOS entitlements`);
|
|
3040
3312
|
return;
|
|
3041
3313
|
}
|
|
3042
|
-
const
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
|
|
3047
|
-
|
|
3048
|
-
|
|
3049
|
-
|
|
3050
|
-
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
|
|
3055
|
-
|
|
3056
|
-
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
|
|
3314
|
+
const filePath = findEntitlementsFile(appRoot) || createEntitlementsFile(appRoot, plistPath);
|
|
3315
|
+
if (!filePath) {
|
|
3316
|
+
warn(`no .entitlements file next to ${INFO_PLIST}, add one to enable ${wanted.map(([key]) => key).join(", ")}`);
|
|
3317
|
+
return;
|
|
3318
|
+
}
|
|
3319
|
+
patchPlistFile(filePath, wanted);
|
|
3320
|
+
}
|
|
3321
|
+
module.exports = {
|
|
3322
|
+
findInfoPlistFile,
|
|
3323
|
+
patchInfoPlist,
|
|
3324
|
+
patchInfoPlistArrays,
|
|
3325
|
+
patchInfoPlistBooleans,
|
|
3326
|
+
patchEntitlements
|
|
3327
|
+
};
|
|
3328
|
+
}));
|
|
3329
|
+
//#endregion
|
|
3330
|
+
//#region src/grant-bundles.ts
|
|
3331
|
+
var import_src = (/* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
3332
|
+
const fs = __require("node:fs");
|
|
3333
|
+
const path = __require("node:path");
|
|
3334
|
+
const { dlog, warn } = require_common();
|
|
3335
|
+
const { buildGradlePath, findMainApplicationFile } = require_android_paths();
|
|
3336
|
+
const { patchBuildGradle, patchRootBuildGradle, patchMainApplication } = require_android_regions();
|
|
3337
|
+
const { patchAndroidManifest, patchAndroidManifestPermissions, patchAndroidManifestServices, patchMainActivityConfigChanges } = require_android_manifest();
|
|
3338
|
+
const { findInfoPlistFile, patchInfoPlist, patchInfoPlistArrays, patchInfoPlistBooleans, patchEntitlements } = require_ios_plist();
|
|
3339
|
+
const MANIFEST_FILENAME = "native-link.json";
|
|
3340
|
+
function findAppRoot() {
|
|
3341
|
+
const candidates = [process.cwd(), process.env.INIT_CWD].filter(Boolean);
|
|
3342
|
+
for (const candidate of candidates) {
|
|
3343
|
+
if (fs.existsSync(buildGradlePath(candidate))) return candidate;
|
|
3344
|
+
if (findInfoPlistFile(candidate)) return candidate;
|
|
3062
3345
|
}
|
|
3063
|
-
|
|
3346
|
+
return null;
|
|
3347
|
+
}
|
|
3348
|
+
function readManifest(packageDir, packageName) {
|
|
3349
|
+
const manifestPath = path.join(packageDir, MANIFEST_FILENAME);
|
|
3350
|
+
if (!fs.existsSync(manifestPath)) return null;
|
|
3351
|
+
try {
|
|
3352
|
+
return {
|
|
3353
|
+
packageName,
|
|
3354
|
+
manifest: JSON.parse(fs.readFileSync(manifestPath, "utf8"))
|
|
3355
|
+
};
|
|
3356
|
+
} catch (error) {
|
|
3357
|
+
warn(`skipping ${packageName}: its ${MANIFEST_FILENAME} is not valid JSON (${error.message})`);
|
|
3358
|
+
return null;
|
|
3359
|
+
}
|
|
3360
|
+
}
|
|
3361
|
+
function readScopedManifests(modulesRoot, scopeName) {
|
|
3362
|
+
const scopeDir = path.join(modulesRoot, scopeName);
|
|
3363
|
+
return fs.readdirSync(scopeDir, { withFileTypes: true }).map((scoped) => readManifest(path.join(scopeDir, scoped.name), `${scopeName}/${scoped.name}`));
|
|
3364
|
+
}
|
|
3365
|
+
function comparePackageNames(a, b) {
|
|
3366
|
+
if (a.packageName < b.packageName) return -1;
|
|
3367
|
+
return a.packageName > b.packageName ? 1 : 0;
|
|
3368
|
+
}
|
|
3369
|
+
function collectManifests(appRoot) {
|
|
3370
|
+
const modulesRoot = path.join(appRoot, "node_modules");
|
|
3371
|
+
if (!fs.existsSync(modulesRoot)) {
|
|
3372
|
+
warn(`no node_modules in ${appRoot}, nothing to link`);
|
|
3373
|
+
return [];
|
|
3374
|
+
}
|
|
3375
|
+
const found = fs.readdirSync(modulesRoot, { withFileTypes: true }).filter((entry) => !entry.name.startsWith(".")).flatMap((entry) => entry.name.startsWith("@") ? readScopedManifests(modulesRoot, entry.name) : [readManifest(path.join(modulesRoot, entry.name), entry.name)]).filter(Boolean);
|
|
3376
|
+
found.sort(comparePackageNames);
|
|
3377
|
+
dlog(`found ${found.length} linkable package(s): ${found.map((f) => f.packageName).join(", ")}`);
|
|
3378
|
+
return found;
|
|
3064
3379
|
}
|
|
3065
3380
|
function linkApp(explicitAppRoot) {
|
|
3066
3381
|
const appRoot = explicitAppRoot || findAppRoot();
|
|
@@ -3070,6 +3385,7 @@ var import_src = (/* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
3070
3385
|
}
|
|
3071
3386
|
const entries = collectManifests(appRoot);
|
|
3072
3387
|
patchBuildGradle(appRoot, entries);
|
|
3388
|
+
patchRootBuildGradle(appRoot, entries);
|
|
3073
3389
|
patchMainApplication(appRoot, entries);
|
|
3074
3390
|
patchAndroidManifest(appRoot, entries);
|
|
3075
3391
|
patchAndroidManifestPermissions(appRoot, entries);
|
|
@@ -3077,6 +3393,8 @@ var import_src = (/* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
3077
3393
|
patchMainActivityConfigChanges(appRoot, entries);
|
|
3078
3394
|
patchInfoPlist(appRoot, entries);
|
|
3079
3395
|
patchInfoPlistArrays(appRoot, entries);
|
|
3396
|
+
patchInfoPlistBooleans(appRoot, entries);
|
|
3397
|
+
patchEntitlements(appRoot, entries);
|
|
3080
3398
|
}
|
|
3081
3399
|
module.exports = {
|
|
3082
3400
|
linkApp,
|
|
@@ -3085,13 +3403,16 @@ var import_src = (/* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
3085
3403
|
findInfoPlistFile,
|
|
3086
3404
|
findMainApplicationFile,
|
|
3087
3405
|
patchBuildGradle,
|
|
3406
|
+
patchRootBuildGradle,
|
|
3088
3407
|
patchMainApplication,
|
|
3089
3408
|
patchAndroidManifest,
|
|
3090
3409
|
patchAndroidManifestPermissions,
|
|
3091
3410
|
patchAndroidManifestServices,
|
|
3092
3411
|
patchMainActivityConfigChanges,
|
|
3093
3412
|
patchInfoPlist,
|
|
3094
|
-
patchInfoPlistArrays
|
|
3413
|
+
patchInfoPlistArrays,
|
|
3414
|
+
patchInfoPlistBooleans,
|
|
3415
|
+
patchEntitlements
|
|
3095
3416
|
};
|
|
3096
3417
|
})))();
|
|
3097
3418
|
function discoveredBundlesFromLayers(layers) {
|
|
@@ -3582,13 +3903,13 @@ function tryGitInit(root) {
|
|
|
3582
3903
|
async function runNew(parsed) {
|
|
3583
3904
|
intro("@symbiote-native/cli new");
|
|
3584
3905
|
const appName = await resolveAppName(parsed.appName);
|
|
3585
|
-
const targetDir = path$
|
|
3906
|
+
const targetDir = path$5.resolve(process.cwd(), appName);
|
|
3586
3907
|
if (!isEmptyDir(targetDir)) {
|
|
3587
3908
|
if (!await resolveOverwrite(targetDir, parsed.hasForce)) {
|
|
3588
3909
|
cancel("Cancelled.");
|
|
3589
3910
|
process.exit(1);
|
|
3590
3911
|
}
|
|
3591
|
-
fs$
|
|
3912
|
+
fs$6.rmSync(targetDir, {
|
|
3592
3913
|
recursive: true,
|
|
3593
3914
|
force: true
|
|
3594
3915
|
});
|
|
@@ -3614,7 +3935,7 @@ async function runNew(parsed) {
|
|
|
3614
3935
|
styling,
|
|
3615
3936
|
packageManager
|
|
3616
3937
|
});
|
|
3617
|
-
const hasGitDir = fs$
|
|
3938
|
+
const hasGitDir = fs$6.existsSync(path$5.join(root, ".git"));
|
|
3618
3939
|
const lines = [
|
|
3619
3940
|
`Scaffolded "${appName}" at ${root}.`,
|
|
3620
3941
|
"",
|
|
@@ -3645,7 +3966,7 @@ async function runNew(parsed) {
|
|
|
3645
3966
|
}
|
|
3646
3967
|
//#endregion
|
|
3647
3968
|
//#region package.json
|
|
3648
|
-
var version = "0.
|
|
3969
|
+
var version = "0.4.0";
|
|
3649
3970
|
//#endregion
|
|
3650
3971
|
//#region src/index.ts
|
|
3651
3972
|
function printHelp() {
|