@symbiote-native/cli 0.2.1 → 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 +851 -430
- package/package.json +2 -2
- package/templates/layers/README.md +1 -1
- package/templates/layers/age-range/package.json.fragment.json +5 -0
- package/templates/layers/app-integrity/package.json.fragment.json +5 -0
- package/templates/layers/app-metrics/package.json.fragment.json +5 -0
- package/templates/layers/apple-authentication/package.json.fragment.json +5 -0
- package/templates/layers/asset/package.json.fragment.json +5 -0
- package/templates/layers/auth-session/package.json.fragment.json +5 -0
- package/templates/layers/blob/package.json.fragment.json +5 -0
- package/templates/layers/blur/package.json.fragment.json +5 -0
- package/templates/layers/calendar/package.json.fragment.json +5 -0
- package/templates/layers/camera/package.json.fragment.json +5 -0
- package/templates/layers/constants/package.json.fragment.json +5 -0
- package/templates/layers/contacts/package.json.fragment.json +5 -0
- package/templates/layers/document-picker/package.json.fragment.json +5 -0
- package/templates/layers/font/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/image-manipulator/package.json.fragment.json +5 -0
- package/templates/layers/image-picker/package.json.fragment.json +5 -0
- package/templates/layers/intent-launcher/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/mail-composer/package.json.fragment.json +5 -0
- package/templates/layers/navigation-bar/package.json.fragment.json +5 -0
- package/templates/layers/print/package.json.fragment.json +5 -0
- package/templates/layers/screen-capture/package.json.fragment.json +5 -0
- package/templates/layers/speech/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/templates/layers/video-thumbnails/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";
|
|
@@ -117,11 +117,36 @@ var NotSymbioteAppError = class extends Error {
|
|
|
117
117
|
//#endregion
|
|
118
118
|
//#region src/expo-package-layers.ts
|
|
119
119
|
const EXPO_PACKAGE_LAYERS = [
|
|
120
|
+
{
|
|
121
|
+
id: "age-range",
|
|
122
|
+
label: "Age range",
|
|
123
|
+
symbiotePackage: "@symbiote-native/age-range"
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
id: "app-integrity",
|
|
127
|
+
label: "App integrity",
|
|
128
|
+
symbiotePackage: "@symbiote-native/app-integrity"
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
id: "app-metrics",
|
|
132
|
+
label: "App metrics",
|
|
133
|
+
symbiotePackage: "@symbiote-native/app-metrics"
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
id: "apple-authentication",
|
|
137
|
+
label: "Sign in with Apple",
|
|
138
|
+
symbiotePackage: "@symbiote-native/apple-authentication"
|
|
139
|
+
},
|
|
120
140
|
{
|
|
121
141
|
id: "application",
|
|
122
142
|
label: "Application info",
|
|
123
143
|
symbiotePackage: "@symbiote-native/application"
|
|
124
144
|
},
|
|
145
|
+
{
|
|
146
|
+
id: "asset",
|
|
147
|
+
label: "Asset",
|
|
148
|
+
symbiotePackage: "@symbiote-native/asset"
|
|
149
|
+
},
|
|
125
150
|
{
|
|
126
151
|
id: "audio",
|
|
127
152
|
label: "Audio",
|
|
@@ -138,6 +163,11 @@ const EXPO_PACKAGE_LAYERS = [
|
|
|
138
163
|
}]
|
|
139
164
|
}]
|
|
140
165
|
},
|
|
166
|
+
{
|
|
167
|
+
id: "auth-session",
|
|
168
|
+
label: "Auth session (OAuth2/OIDC)",
|
|
169
|
+
symbiotePackage: "@symbiote-native/auth-session"
|
|
170
|
+
},
|
|
141
171
|
{
|
|
142
172
|
id: "background-fetch",
|
|
143
173
|
label: "Background fetch",
|
|
@@ -153,11 +183,31 @@ const EXPO_PACKAGE_LAYERS = [
|
|
|
153
183
|
label: "Battery",
|
|
154
184
|
symbiotePackage: "@symbiote-native/battery"
|
|
155
185
|
},
|
|
186
|
+
{
|
|
187
|
+
id: "blob",
|
|
188
|
+
label: "Blob",
|
|
189
|
+
symbiotePackage: "@symbiote-native/blob"
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
id: "blur",
|
|
193
|
+
label: "Blur",
|
|
194
|
+
symbiotePackage: "@symbiote-native/blur"
|
|
195
|
+
},
|
|
156
196
|
{
|
|
157
197
|
id: "brightness",
|
|
158
198
|
label: "Brightness",
|
|
159
199
|
symbiotePackage: "@symbiote-native/brightness"
|
|
160
200
|
},
|
|
201
|
+
{
|
|
202
|
+
id: "calendar",
|
|
203
|
+
label: "Calendar",
|
|
204
|
+
symbiotePackage: "@symbiote-native/calendar"
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
id: "camera",
|
|
208
|
+
label: "Camera",
|
|
209
|
+
symbiotePackage: "@symbiote-native/camera"
|
|
210
|
+
},
|
|
161
211
|
{
|
|
162
212
|
id: "cellular",
|
|
163
213
|
label: "Cellular info",
|
|
@@ -168,6 +218,16 @@ const EXPO_PACKAGE_LAYERS = [
|
|
|
168
218
|
label: "Clipboard",
|
|
169
219
|
symbiotePackage: "@symbiote-native/clipboard"
|
|
170
220
|
},
|
|
221
|
+
{
|
|
222
|
+
id: "constants",
|
|
223
|
+
label: "Constants",
|
|
224
|
+
symbiotePackage: "@symbiote-native/constants"
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
id: "contacts",
|
|
228
|
+
label: "Contacts",
|
|
229
|
+
symbiotePackage: "@symbiote-native/contacts"
|
|
230
|
+
},
|
|
171
231
|
{
|
|
172
232
|
id: "crypto",
|
|
173
233
|
label: "Crypto",
|
|
@@ -178,21 +238,71 @@ const EXPO_PACKAGE_LAYERS = [
|
|
|
178
238
|
label: "Device info",
|
|
179
239
|
symbiotePackage: "@symbiote-native/device"
|
|
180
240
|
},
|
|
241
|
+
{
|
|
242
|
+
id: "document-picker",
|
|
243
|
+
label: "Document picker",
|
|
244
|
+
symbiotePackage: "@symbiote-native/document-picker"
|
|
245
|
+
},
|
|
181
246
|
{
|
|
182
247
|
id: "file-system",
|
|
183
248
|
label: "File system",
|
|
184
249
|
symbiotePackage: "@symbiote-native/file-system"
|
|
185
250
|
},
|
|
251
|
+
{
|
|
252
|
+
id: "font",
|
|
253
|
+
label: "Font",
|
|
254
|
+
symbiotePackage: "@symbiote-native/font"
|
|
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
|
+
},
|
|
186
266
|
{
|
|
187
267
|
id: "haptics",
|
|
188
268
|
label: "Haptics",
|
|
189
269
|
symbiotePackage: "@symbiote-native/haptics"
|
|
190
270
|
},
|
|
271
|
+
{
|
|
272
|
+
id: "image",
|
|
273
|
+
label: "Image",
|
|
274
|
+
symbiotePackage: "@symbiote-native/image"
|
|
275
|
+
},
|
|
276
|
+
{
|
|
277
|
+
id: "image-manipulator",
|
|
278
|
+
label: "Image manipulator",
|
|
279
|
+
symbiotePackage: "@symbiote-native/image-manipulator"
|
|
280
|
+
},
|
|
281
|
+
{
|
|
282
|
+
id: "image-picker",
|
|
283
|
+
label: "Image picker",
|
|
284
|
+
symbiotePackage: "@symbiote-native/image-picker"
|
|
285
|
+
},
|
|
286
|
+
{
|
|
287
|
+
id: "intent-launcher",
|
|
288
|
+
label: "Intent launcher",
|
|
289
|
+
symbiotePackage: "@symbiote-native/intent-launcher"
|
|
290
|
+
},
|
|
191
291
|
{
|
|
192
292
|
id: "keep-awake",
|
|
193
293
|
label: "Keep awake",
|
|
194
294
|
symbiotePackage: "@symbiote-native/keep-awake"
|
|
195
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
|
+
},
|
|
196
306
|
{
|
|
197
307
|
id: "local-auth",
|
|
198
308
|
label: "Local authentication",
|
|
@@ -219,11 +329,21 @@ const EXPO_PACKAGE_LAYERS = [
|
|
|
219
329
|
]
|
|
220
330
|
}]
|
|
221
331
|
},
|
|
332
|
+
{
|
|
333
|
+
id: "mail-composer",
|
|
334
|
+
label: "Mail composer",
|
|
335
|
+
symbiotePackage: "@symbiote-native/mail-composer"
|
|
336
|
+
},
|
|
222
337
|
{
|
|
223
338
|
id: "media-library",
|
|
224
339
|
label: "Media library",
|
|
225
340
|
symbiotePackage: "@symbiote-native/media-library"
|
|
226
341
|
},
|
|
342
|
+
{
|
|
343
|
+
id: "navigation-bar",
|
|
344
|
+
label: "Navigation bar",
|
|
345
|
+
symbiotePackage: "@symbiote-native/navigation-bar"
|
|
346
|
+
},
|
|
227
347
|
{
|
|
228
348
|
id: "network",
|
|
229
349
|
label: "Network info",
|
|
@@ -234,6 +354,16 @@ const EXPO_PACKAGE_LAYERS = [
|
|
|
234
354
|
label: "Notifications",
|
|
235
355
|
symbiotePackage: "@symbiote-native/notifications"
|
|
236
356
|
},
|
|
357
|
+
{
|
|
358
|
+
id: "print",
|
|
359
|
+
label: "Print",
|
|
360
|
+
symbiotePackage: "@symbiote-native/print"
|
|
361
|
+
},
|
|
362
|
+
{
|
|
363
|
+
id: "screen-capture",
|
|
364
|
+
label: "Screen capture",
|
|
365
|
+
symbiotePackage: "@symbiote-native/screen-capture"
|
|
366
|
+
},
|
|
237
367
|
{
|
|
238
368
|
id: "screen-orientation",
|
|
239
369
|
label: "Screen orientation",
|
|
@@ -259,6 +389,11 @@ const EXPO_PACKAGE_LAYERS = [
|
|
|
259
389
|
label: "SMS",
|
|
260
390
|
symbiotePackage: "@symbiote-native/sms"
|
|
261
391
|
},
|
|
392
|
+
{
|
|
393
|
+
id: "speech",
|
|
394
|
+
label: "Speech",
|
|
395
|
+
symbiotePackage: "@symbiote-native/speech"
|
|
396
|
+
},
|
|
262
397
|
{
|
|
263
398
|
id: "sqlite",
|
|
264
399
|
label: "SQLite",
|
|
@@ -274,6 +409,11 @@ const EXPO_PACKAGE_LAYERS = [
|
|
|
274
409
|
label: "Store review prompt",
|
|
275
410
|
symbiotePackage: "@symbiote-native/store-review"
|
|
276
411
|
},
|
|
412
|
+
{
|
|
413
|
+
id: "symbols",
|
|
414
|
+
label: "Symbols",
|
|
415
|
+
symbiotePackage: "@symbiote-native/symbols"
|
|
416
|
+
},
|
|
277
417
|
{
|
|
278
418
|
id: "system-ui",
|
|
279
419
|
label: "System UI",
|
|
@@ -289,6 +429,28 @@ const EXPO_PACKAGE_LAYERS = [
|
|
|
289
429
|
label: "Tracking transparency",
|
|
290
430
|
symbiotePackage: "@symbiote-native/tracking-transparency"
|
|
291
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
|
+
},
|
|
449
|
+
{
|
|
450
|
+
id: "video-thumbnails",
|
|
451
|
+
label: "Video thumbnails",
|
|
452
|
+
symbiotePackage: "@symbiote-native/video-thumbnails"
|
|
453
|
+
},
|
|
292
454
|
{
|
|
293
455
|
id: "web-browser",
|
|
294
456
|
label: "Web browser",
|
|
@@ -1785,44 +1947,44 @@ const TEXT_FILE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
|
1785
1947
|
""
|
|
1786
1948
|
]);
|
|
1787
1949
|
function replaceTextContent(filePath, identity) {
|
|
1788
|
-
const content = fs$
|
|
1950
|
+
const content = fs$6.readFileSync(filePath, "utf8");
|
|
1789
1951
|
const replaced = content.split("com.canary").join(identity.bundleId).split("Canary").join(identity.nativeAppName);
|
|
1790
|
-
if (replaced !== content) fs$
|
|
1952
|
+
if (replaced !== content) fs$6.writeFileSync(filePath, replaced);
|
|
1791
1953
|
}
|
|
1792
1954
|
function walkTextFiles(dir, identity) {
|
|
1793
|
-
for (const entry of fs$
|
|
1794
|
-
const entryPath = path$
|
|
1955
|
+
for (const entry of fs$6.readdirSync(dir, { withFileTypes: true })) {
|
|
1956
|
+
const entryPath = path$5.join(dir, entry.name);
|
|
1795
1957
|
if (entry.isDirectory()) {
|
|
1796
1958
|
walkTextFiles(entryPath, identity);
|
|
1797
1959
|
continue;
|
|
1798
1960
|
}
|
|
1799
|
-
if (TEXT_FILE_EXTENSIONS.has(path$
|
|
1961
|
+
if (TEXT_FILE_EXTENSIONS.has(path$5.extname(entry.name))) replaceTextContent(entryPath, identity);
|
|
1800
1962
|
}
|
|
1801
1963
|
}
|
|
1802
1964
|
function renameCanaryPaths(dir, nativeAppName) {
|
|
1803
|
-
for (const entry of fs$
|
|
1804
|
-
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 })) {
|
|
1805
1967
|
if (!entry.name.startsWith("Canary")) continue;
|
|
1806
|
-
const from = path$
|
|
1807
|
-
const to = path$
|
|
1808
|
-
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);
|
|
1809
1971
|
}
|
|
1810
1972
|
}
|
|
1811
1973
|
function moveAndroidPackageDir(javaSrcDir, bundleId) {
|
|
1812
|
-
const oldDir = path$
|
|
1813
|
-
if (!fs$
|
|
1814
|
-
const newDir = path$
|
|
1815
|
-
fs$
|
|
1816
|
-
fs$
|
|
1817
|
-
const oldTopDir = path$
|
|
1818
|
-
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);
|
|
1819
1981
|
}
|
|
1820
1982
|
function applyAppIdentity(root, identity) {
|
|
1821
1983
|
walkTextFiles(root, identity);
|
|
1822
|
-
const iosDir = path$
|
|
1823
|
-
if (fs$
|
|
1824
|
-
const androidAppSrc = path$
|
|
1825
|
-
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);
|
|
1826
1988
|
}
|
|
1827
1989
|
//#endregion
|
|
1828
1990
|
//#region src/utils/json.ts
|
|
@@ -1869,7 +2031,7 @@ function sortDependencies(packageJson) {
|
|
|
1869
2031
|
//#endregion
|
|
1870
2032
|
//#region src/utils/render-template.ts
|
|
1871
2033
|
function readJsonObject(filePath) {
|
|
1872
|
-
const parsed = JSON.parse(fs$
|
|
2034
|
+
const parsed = JSON.parse(fs$6.readFileSync(filePath, "utf8"));
|
|
1873
2035
|
if (!isJsonObject(parsed)) throw new Error(`${filePath}: expected a JSON object`);
|
|
1874
2036
|
return parsed;
|
|
1875
2037
|
}
|
|
@@ -1899,37 +2061,37 @@ function stripTypescriptInfix(filename) {
|
|
|
1899
2061
|
}
|
|
1900
2062
|
const DEFAULT_OPTIONS = { hasTypescript: true };
|
|
1901
2063
|
function renderTemplate(src, dest, options = DEFAULT_OPTIONS) {
|
|
1902
|
-
if (fs$
|
|
1903
|
-
fs$
|
|
1904
|
-
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);
|
|
1905
2067
|
return;
|
|
1906
2068
|
}
|
|
1907
|
-
const filename = path$
|
|
2069
|
+
const filename = path$5.basename(src);
|
|
1908
2070
|
if (filename === "README.md") return;
|
|
1909
2071
|
if (!options.hasTypescript && (isTypescriptOnlyFile(filename) || filename.includes(".typescript."))) return;
|
|
1910
2072
|
if (options.hasTypescript && isJavascriptOnlyFile(filename)) return;
|
|
1911
2073
|
const destFilename = filename.includes(".typescript.") ? stripTypescriptInfix(filename) : filename;
|
|
1912
|
-
const resolvedDest = path$
|
|
2074
|
+
const resolvedDest = path$5.join(path$5.dirname(dest), destFilename);
|
|
1913
2075
|
if (destFilename === "package.json.fragment.json") {
|
|
1914
|
-
const destPackageJsonPath = path$
|
|
2076
|
+
const destPackageJsonPath = path$5.resolve(path$5.dirname(resolvedDest), "package.json");
|
|
1915
2077
|
const fragment = readJsonObject(src);
|
|
1916
|
-
const merged = sortDependencies(deepMerge(fs$
|
|
1917
|
-
fs$
|
|
2078
|
+
const merged = sortDependencies(deepMerge(fs$6.existsSync(destPackageJsonPath) ? readJsonObject(destPackageJsonPath) : {}, fragment));
|
|
2079
|
+
fs$6.writeFileSync(destPackageJsonPath, `${JSON.stringify(merged, null, 2)}\n`);
|
|
1918
2080
|
return;
|
|
1919
2081
|
}
|
|
1920
|
-
if (destFilename === ".gitignore" && fs$
|
|
1921
|
-
const existing = fs$
|
|
1922
|
-
const addition = fs$
|
|
1923
|
-
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}`);
|
|
1924
2086
|
return;
|
|
1925
2087
|
}
|
|
1926
|
-
fs$
|
|
2088
|
+
fs$6.copyFileSync(src, resolvedDest);
|
|
1927
2089
|
}
|
|
1928
2090
|
//#endregion
|
|
1929
2091
|
//#region src/utils/apply-bootsplash-logo.ts
|
|
1930
2092
|
function applyBootsplashLogo(root, templatesRoot, framework) {
|
|
1931
|
-
const overlayRoot = path$
|
|
1932
|
-
if (fs$
|
|
2093
|
+
const overlayRoot = path$5.join(templatesRoot, "bootsplash-logo", framework);
|
|
2094
|
+
if (fs$6.existsSync(overlayRoot)) renderTemplate(overlayRoot, root);
|
|
1933
2095
|
}
|
|
1934
2096
|
//#endregion
|
|
1935
2097
|
//#region src/utils/apply-expo-modules-manifest.ts
|
|
@@ -1944,54 +2106,54 @@ const EXPO_MODULES_BACKUP_ATTRS = `android:supportsRtl="true"
|
|
|
1944
2106
|
android:dataExtractionRules="@xml/secure_store_data_extraction_rules"
|
|
1945
2107
|
android:fullBackupContent="@xml/secure_store_backup_rules">`;
|
|
1946
2108
|
function applyExpoModulesAndroidManifest(root) {
|
|
1947
|
-
const manifestPath = path$
|
|
1948
|
-
const manifest = fs$
|
|
2109
|
+
const manifestPath = path$5.join(root, "android/app/src/main/AndroidManifest.xml");
|
|
2110
|
+
const manifest = fs$6.readFileSync(manifestPath, "utf8");
|
|
1949
2111
|
const withPermissions = manifest.includes("USE_BIOMETRIC") ? manifest : manifest.replace(" <uses-permission android:name=\"android.permission.VIBRATE\" />", EXPO_MODULES_PERMISSIONS);
|
|
1950
2112
|
const withBackupAttrs = withPermissions.includes("dataExtractionRules") ? withPermissions : withPermissions.replace("android:supportsRtl=\"true\">", EXPO_MODULES_BACKUP_ATTRS);
|
|
1951
|
-
fs$
|
|
2113
|
+
fs$6.writeFileSync(manifestPath, withBackupAttrs);
|
|
1952
2114
|
}
|
|
1953
2115
|
//#endregion
|
|
1954
2116
|
//#region src/utils/apply-expo-modules-podfile-autolinking.ts
|
|
1955
2117
|
const AUTOLINKING_REQUIRE = "require File.join(File.dirname(`node --print \"require.resolve('expo/package.json')\"`), \"scripts/autolinking\")";
|
|
1956
2118
|
const NATIVE_MODULES_LINE = "config = use_native_modules!";
|
|
1957
2119
|
function applyExpoModulesPodfileAutolinking(root) {
|
|
1958
|
-
const podfilePath = path$
|
|
1959
|
-
if (!fs$
|
|
1960
|
-
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");
|
|
1961
2123
|
if (podfile.includes("use_expo_modules!")) return;
|
|
1962
2124
|
if (!podfile.includes(AUTOLINKING_REQUIRE)) podfile = `${AUTOLINKING_REQUIRE}\n\n${podfile}`;
|
|
1963
2125
|
podfile = podfile.replace(NATIVE_MODULES_LINE, `${NATIVE_MODULES_LINE}\n\n use_expo_modules!`);
|
|
1964
|
-
fs$
|
|
2126
|
+
fs$6.writeFileSync(podfilePath, podfile);
|
|
1965
2127
|
}
|
|
1966
2128
|
//#endregion
|
|
1967
2129
|
//#region src/utils/apply-expo-modules-podfile.ts
|
|
1968
2130
|
const DEFAULT_PLATFORM_LINE = "platform :ios, min_ios_version_supported.to_s";
|
|
1969
2131
|
const RAISED_PLATFORM_LINE = "platform :ios, [min_ios_version_supported.to_f, 16.4].max.to_s";
|
|
1970
2132
|
function applyExpoModulesPodfilePlatform(root) {
|
|
1971
|
-
const podfilePath = path$
|
|
1972
|
-
if (!fs$
|
|
1973
|
-
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");
|
|
1974
2136
|
if (!podfile.includes(DEFAULT_PLATFORM_LINE)) return;
|
|
1975
|
-
fs$
|
|
2137
|
+
fs$6.writeFileSync(podfilePath, podfile.replace(DEFAULT_PLATFORM_LINE, RAISED_PLATFORM_LINE));
|
|
1976
2138
|
}
|
|
1977
2139
|
//#endregion
|
|
1978
2140
|
//#region src/utils/apply-expo-modules-xcode-deployment-target.ts
|
|
1979
2141
|
const MIN_EXPO_IOS_VERSION = 16.4;
|
|
1980
2142
|
const DEPLOYMENT_TARGET_LINE = /IPHONEOS_DEPLOYMENT_TARGET = (\d+(?:\.\d+)?);/g;
|
|
1981
2143
|
function findPbxprojPath(root) {
|
|
1982
|
-
const iosDir = path$
|
|
1983
|
-
if (!fs$
|
|
1984
|
-
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"));
|
|
1985
2147
|
if (!xcodeproj) return void 0;
|
|
1986
|
-
const pbxprojPath = path$
|
|
1987
|
-
return fs$
|
|
2148
|
+
const pbxprojPath = path$5.join(iosDir, xcodeproj, "project.pbxproj");
|
|
2149
|
+
return fs$6.existsSync(pbxprojPath) ? pbxprojPath : void 0;
|
|
1988
2150
|
}
|
|
1989
2151
|
function applyExpoModulesXcodeDeploymentTarget(root) {
|
|
1990
2152
|
const pbxprojPath = findPbxprojPath(root);
|
|
1991
2153
|
if (!pbxprojPath) return;
|
|
1992
|
-
const pbxproj = fs$
|
|
2154
|
+
const pbxproj = fs$6.readFileSync(pbxprojPath, "utf8");
|
|
1993
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);
|
|
1994
|
-
if (raised !== pbxproj) fs$
|
|
2156
|
+
if (raised !== pbxproj) fs$6.writeFileSync(pbxprojPath, raised);
|
|
1995
2157
|
}
|
|
1996
2158
|
//#endregion
|
|
1997
2159
|
//#region src/utils/apply-splash-screen-hide.ts
|
|
@@ -2008,8 +2170,8 @@ const APP_COMPONENT_FILENAMES = /* @__PURE__ */ new Set([
|
|
|
2008
2170
|
"App.svelte"
|
|
2009
2171
|
]);
|
|
2010
2172
|
function findAppComponentFile(dir) {
|
|
2011
|
-
for (const entry of fs$
|
|
2012
|
-
const entryPath = path$
|
|
2173
|
+
for (const entry of fs$6.readdirSync(dir, { withFileTypes: true })) {
|
|
2174
|
+
const entryPath = path$5.join(dir, entry.name);
|
|
2013
2175
|
if (entry.isDirectory()) {
|
|
2014
2176
|
if (SKIPPED_DIR_NAMES$1.has(entry.name)) continue;
|
|
2015
2177
|
const found = findAppComponentFile(entryPath);
|
|
@@ -2055,9 +2217,9 @@ function patchSvelte(source) {
|
|
|
2055
2217
|
function applySplashScreenAppHide(root, options) {
|
|
2056
2218
|
const appFilePath = findAppComponentFile(root);
|
|
2057
2219
|
if (appFilePath === void 0) return;
|
|
2058
|
-
const source = fs$
|
|
2220
|
+
const source = fs$6.readFileSync(appFilePath, "utf8");
|
|
2059
2221
|
if (/\bhide\(\)/.test(source)) return;
|
|
2060
|
-
fs$
|
|
2222
|
+
fs$6.writeFileSync(appFilePath, patch(source, options.framework, appFilePath));
|
|
2061
2223
|
}
|
|
2062
2224
|
function patch(source, framework, appFilePath) {
|
|
2063
2225
|
switch (framework) {
|
|
@@ -2073,16 +2235,16 @@ function patch(source, framework, appFilePath) {
|
|
|
2073
2235
|
const INLINE_STYLE_BLOCK_FRAMEWORKS = /* @__PURE__ */ new Set(["vue-sfc", "svelte"]);
|
|
2074
2236
|
const STYLE_TAG_PATTERN = /<style(?=[ >])/;
|
|
2075
2237
|
function addStyleLangAttribute(dir, lang) {
|
|
2076
|
-
for (const entry of fs$
|
|
2077
|
-
const entryPath = path$
|
|
2238
|
+
for (const entry of fs$6.readdirSync(dir, { withFileTypes: true })) {
|
|
2239
|
+
const entryPath = path$5.join(dir, entry.name);
|
|
2078
2240
|
if (entry.isDirectory()) {
|
|
2079
2241
|
if (SKIPPED_DIR_NAMES.has(entry.name)) continue;
|
|
2080
2242
|
addStyleLangAttribute(entryPath, lang);
|
|
2081
2243
|
continue;
|
|
2082
2244
|
}
|
|
2083
2245
|
if (!entry.name.endsWith(".vue") && !entry.name.endsWith(".svelte")) continue;
|
|
2084
|
-
const content = fs$
|
|
2085
|
-
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}"`));
|
|
2086
2248
|
}
|
|
2087
2249
|
}
|
|
2088
2250
|
const PREPROCESSOR_EXTENSIONS = {
|
|
@@ -2096,42 +2258,42 @@ const SKIPPED_DIR_NAMES = /* @__PURE__ */ new Set([
|
|
|
2096
2258
|
"node_modules"
|
|
2097
2259
|
]);
|
|
2098
2260
|
function findAppCssFile(dir) {
|
|
2099
|
-
for (const entry of fs$
|
|
2261
|
+
for (const entry of fs$6.readdirSync(dir, { withFileTypes: true })) {
|
|
2100
2262
|
if (entry.isDirectory()) {
|
|
2101
2263
|
if (SKIPPED_DIR_NAMES.has(entry.name)) continue;
|
|
2102
|
-
const found = findAppCssFile(path$
|
|
2264
|
+
const found = findAppCssFile(path$5.join(dir, entry.name));
|
|
2103
2265
|
if (found !== void 0) return found;
|
|
2104
2266
|
continue;
|
|
2105
2267
|
}
|
|
2106
|
-
if (entry.name === "App.css") return path$
|
|
2268
|
+
if (entry.name === "App.css") return path$5.join(dir, entry.name);
|
|
2107
2269
|
}
|
|
2108
2270
|
}
|
|
2109
2271
|
function replaceInTextFiles(dir, from, to) {
|
|
2110
|
-
for (const entry of fs$
|
|
2111
|
-
const entryPath = path$
|
|
2272
|
+
for (const entry of fs$6.readdirSync(dir, { withFileTypes: true })) {
|
|
2273
|
+
const entryPath = path$5.join(dir, entry.name);
|
|
2112
2274
|
if (entry.isDirectory()) {
|
|
2113
2275
|
if (SKIPPED_DIR_NAMES.has(entry.name)) continue;
|
|
2114
2276
|
replaceInTextFiles(entryPath, from, to);
|
|
2115
2277
|
continue;
|
|
2116
2278
|
}
|
|
2117
|
-
const content = fs$
|
|
2118
|
-
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));
|
|
2119
2281
|
}
|
|
2120
2282
|
}
|
|
2121
2283
|
function renameStylesheetExtension(root, extension) {
|
|
2122
2284
|
const cssFile = findAppCssFile(root);
|
|
2123
2285
|
if (cssFile === void 0) return;
|
|
2124
|
-
fs$
|
|
2286
|
+
fs$6.renameSync(cssFile, path$5.join(path$5.dirname(cssFile), `App.${extension}`));
|
|
2125
2287
|
replaceInTextFiles(root, "'./App.css'", `'./App.${extension}'`);
|
|
2126
2288
|
}
|
|
2127
2289
|
function applyStyling(root, options) {
|
|
2128
2290
|
const { styling, jsTemplateDir, hasNavigation, hasTypescript, templatesRoot } = options;
|
|
2129
2291
|
if (styling === "css") return;
|
|
2130
2292
|
if (styling === "css-modules" || styling === "stylesheet") {
|
|
2131
|
-
renderTemplate(path$
|
|
2132
|
-
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 });
|
|
2133
2295
|
const orphanedCss = findAppCssFile(root);
|
|
2134
|
-
if (orphanedCss !== void 0) fs$
|
|
2296
|
+
if (orphanedCss !== void 0) fs$6.rmSync(orphanedCss, { force: true });
|
|
2135
2297
|
return;
|
|
2136
2298
|
}
|
|
2137
2299
|
if (INLINE_STYLE_BLOCK_FRAMEWORKS.has(jsTemplateDir)) {
|
|
@@ -2170,50 +2332,50 @@ var TargetDirNotEmptyError = class extends Error {
|
|
|
2170
2332
|
}
|
|
2171
2333
|
};
|
|
2172
2334
|
function resolveTemplatesRoot() {
|
|
2173
|
-
const packageDir = path$
|
|
2174
|
-
const sibling = path$
|
|
2175
|
-
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");
|
|
2176
2338
|
}
|
|
2177
2339
|
const templatesRoot = resolveTemplatesRoot();
|
|
2178
2340
|
function isEmptyDir(dir) {
|
|
2179
|
-
if (!fs$
|
|
2180
|
-
const entries = fs$
|
|
2341
|
+
if (!fs$6.existsSync(dir)) return true;
|
|
2342
|
+
const entries = fs$6.readdirSync(dir);
|
|
2181
2343
|
return entries.length === 0 || entries.length === 1 && entries[0] === ".git";
|
|
2182
2344
|
}
|
|
2183
2345
|
function scaffoldApp(options) {
|
|
2184
|
-
const root = path$
|
|
2346
|
+
const root = path$5.resolve(process.cwd(), options.appName);
|
|
2185
2347
|
const jsTemplateDir = resolveJsTemplateDir(options.framework, options.vueFlavor);
|
|
2186
2348
|
if (!isEmptyDir(root)) throw new TargetDirNotEmptyError(root);
|
|
2187
|
-
fs$
|
|
2188
|
-
const packageNameSource = options.appName === "." ? path$
|
|
2189
|
-
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({
|
|
2190
2352
|
name: toValidPackageName(packageNameSource),
|
|
2191
2353
|
version: "0.0.1",
|
|
2192
2354
|
private: true
|
|
2193
2355
|
}, null, 2)}\n`);
|
|
2194
|
-
renderTemplate(path$
|
|
2195
|
-
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 });
|
|
2196
2358
|
applyBootsplashLogo(root, templatesRoot, options.framework);
|
|
2197
2359
|
const hasExpoModules = options.hasExpoModules || (options.expoPackages?.size ?? 0) > 0;
|
|
2198
2360
|
if (options.hasNavigation) {
|
|
2199
|
-
const navigationLayerRoot = path$
|
|
2200
|
-
renderTemplate(path$
|
|
2201
|
-
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 });
|
|
2202
2364
|
}
|
|
2203
|
-
if (hasExpoModules) renderTemplate(path$
|
|
2365
|
+
if (hasExpoModules) renderTemplate(path$5.join(templatesRoot, "layers", "expo-modules"), root);
|
|
2204
2366
|
if (options.hasSplashScreen) {
|
|
2205
|
-
const splashScreenLayerRoot = path$
|
|
2206
|
-
renderTemplate(path$
|
|
2207
|
-
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);
|
|
2208
2370
|
}
|
|
2209
|
-
if (options.hasSlider) renderTemplate(path$
|
|
2210
|
-
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"));
|
|
2211
2373
|
if (options.hasTesting) {
|
|
2212
|
-
const testingLayerRoot = path$
|
|
2374
|
+
const testingLayerRoot = path$5.join(templatesRoot, "layers", "testing");
|
|
2213
2375
|
const fragmentName = options.framework === "angular" ? "angular" : "base";
|
|
2214
|
-
renderTemplate(path$
|
|
2215
|
-
renderTemplate(path$
|
|
2216
|
-
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 });
|
|
2217
2379
|
}
|
|
2218
2380
|
if (hasExpoModules) {
|
|
2219
2381
|
applyExpoModulesAndroidManifest(root);
|
|
@@ -2238,9 +2400,9 @@ function scaffoldApp(options) {
|
|
|
2238
2400
|
//#endregion
|
|
2239
2401
|
//#region src/utils/find-ios-app-dir.ts
|
|
2240
2402
|
function findIosAppDir(root) {
|
|
2241
|
-
const iosDir = path$
|
|
2242
|
-
if (!fs$
|
|
2243
|
-
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);
|
|
2244
2406
|
}
|
|
2245
2407
|
//#endregion
|
|
2246
2408
|
//#region src/utils/apply-expo-modules-plist.ts
|
|
@@ -2252,14 +2414,14 @@ const USAGE_DESCRIPTIONS = [
|
|
|
2252
2414
|
function applyExpoModulesInfoPlist(root) {
|
|
2253
2415
|
const iosAppDir = findIosAppDir(root);
|
|
2254
2416
|
if (iosAppDir === void 0) return;
|
|
2255
|
-
const plistPath = path$
|
|
2256
|
-
if (!fs$
|
|
2257
|
-
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");
|
|
2258
2420
|
const updated = USAGE_DESCRIPTIONS.reduce((current, [key, description]) => {
|
|
2259
2421
|
if (current.includes(`<key>${key}</key>`)) return current;
|
|
2260
2422
|
return current.replace("</dict>\n</plist>", `\t<key>${key}</key>\n\t<string>${description}</string>\n</dict>\n</plist>`);
|
|
2261
2423
|
}, plist);
|
|
2262
|
-
if (updated !== plist) fs$
|
|
2424
|
+
if (updated !== plist) fs$6.writeFileSync(plistPath, updated);
|
|
2263
2425
|
}
|
|
2264
2426
|
//#endregion
|
|
2265
2427
|
//#region src/utils/apply-splash-screen-app-delegate.ts
|
|
@@ -2268,9 +2430,9 @@ const REACT_IMPORT = "import React\n";
|
|
|
2268
2430
|
function applySplashScreenAppDelegate(root) {
|
|
2269
2431
|
const iosAppDir = findIosAppDir(root);
|
|
2270
2432
|
if (iosAppDir === void 0) return;
|
|
2271
|
-
const appDelegatePath = path$
|
|
2272
|
-
if (!fs$
|
|
2273
|
-
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");
|
|
2274
2436
|
if (source.includes("RNBootSplash.initWithStoryboard(")) return;
|
|
2275
2437
|
if (!source.includes(REACT_IMPORT)) return;
|
|
2276
2438
|
const updated = source.includes("import RNBootSplash") ? source : source.replace(REACT_IMPORT, `${REACT_IMPORT}import RNBootSplash\n`);
|
|
@@ -2279,13 +2441,13 @@ function applySplashScreenAppDelegate(root) {
|
|
|
2279
2441
|
}
|
|
2280
2442
|
|
|
2281
2443
|
`);
|
|
2282
|
-
fs$
|
|
2444
|
+
fs$6.writeFileSync(appDelegatePath, withInit);
|
|
2283
2445
|
}
|
|
2284
2446
|
//#endregion
|
|
2285
2447
|
//#region src/utils/apply-splash-screen-main-activity.ts
|
|
2286
2448
|
function findMainActivityKt(dir) {
|
|
2287
|
-
for (const entry of fs$
|
|
2288
|
-
const entryPath = path$
|
|
2449
|
+
for (const entry of fs$6.readdirSync(dir, { withFileTypes: true })) {
|
|
2450
|
+
const entryPath = path$5.join(dir, entry.name);
|
|
2289
2451
|
if (entry.isDirectory()) {
|
|
2290
2452
|
const found = findMainActivityKt(entryPath);
|
|
2291
2453
|
if (found !== void 0) return found;
|
|
@@ -2297,32 +2459,32 @@ function findMainActivityKt(dir) {
|
|
|
2297
2459
|
const ONCREATE_OPEN = /override fun onCreate\(savedInstanceState: Bundle\?\) \{\n/;
|
|
2298
2460
|
const REACT_ACTIVITY_IMPORT = "import com.facebook.react.ReactActivity\n";
|
|
2299
2461
|
function applySplashScreenMainActivity(root) {
|
|
2300
|
-
const javaDir = path$
|
|
2301
|
-
if (!fs$
|
|
2462
|
+
const javaDir = path$5.join(root, "android/app/src/main/java");
|
|
2463
|
+
if (!fs$6.existsSync(javaDir)) return;
|
|
2302
2464
|
const mainActivityPath = findMainActivityKt(javaDir);
|
|
2303
2465
|
if (mainActivityPath === void 0) return;
|
|
2304
|
-
const source = fs$
|
|
2466
|
+
const source = fs$6.readFileSync(mainActivityPath, "utf8");
|
|
2305
2467
|
if (source.includes("RNBootSplash.init(")) return;
|
|
2306
2468
|
if (!source.includes(REACT_ACTIVITY_IMPORT)) return;
|
|
2307
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`);
|
|
2308
2470
|
if (!updated.includes("import android.os.Bundle")) updated = updated.replace(REACT_ACTIVITY_IMPORT, `import android.os.Bundle\n${REACT_ACTIVITY_IMPORT}`);
|
|
2309
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");
|
|
2310
|
-
fs$
|
|
2472
|
+
fs$6.writeFileSync(mainActivityPath, updated);
|
|
2311
2473
|
}
|
|
2312
2474
|
//#endregion
|
|
2313
2475
|
//#region src/utils/apply-splash-screen-manifest.ts
|
|
2314
2476
|
const MAIN_ACTIVITY_BLOCK = /<activity\b[^>]*android:name="\.MainActivity"[^>]*>/;
|
|
2315
2477
|
function applySplashScreenManifest(root) {
|
|
2316
|
-
const manifestPath = path$
|
|
2317
|
-
if (!fs$
|
|
2318
|
-
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");
|
|
2319
2481
|
const match = manifest.match(MAIN_ACTIVITY_BLOCK);
|
|
2320
2482
|
if (match === null) return;
|
|
2321
2483
|
const block = match[0];
|
|
2322
2484
|
if (block.includes("@style/BootTheme")) return;
|
|
2323
2485
|
if (!block.includes("android:theme=\"@style/AppTheme\"")) return;
|
|
2324
2486
|
const updatedBlock = block.replace("android:theme=\"@style/AppTheme\"", "android:theme=\"@style/BootTheme\"");
|
|
2325
|
-
fs$
|
|
2487
|
+
fs$6.writeFileSync(manifestPath, manifest.replace(block, updatedBlock));
|
|
2326
2488
|
}
|
|
2327
2489
|
//#endregion
|
|
2328
2490
|
//#region src/utils/apply-splash-screen-styles.ts
|
|
@@ -2333,22 +2495,22 @@ const BOOT_THEME_STYLE = ` <style name="BootTheme" parent="Theme.BootSplash">
|
|
|
2333
2495
|
</style>
|
|
2334
2496
|
`;
|
|
2335
2497
|
function applySplashScreenStyles(root) {
|
|
2336
|
-
const stylesPath = path$
|
|
2337
|
-
if (!fs$
|
|
2338
|
-
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");
|
|
2339
2501
|
if (styles.includes("name=\"BootTheme\"")) return;
|
|
2340
2502
|
if (!styles.includes("</resources>")) return;
|
|
2341
|
-
fs$
|
|
2503
|
+
fs$6.writeFileSync(stylesPath, styles.replace("</resources>", `${BOOT_THEME_STYLE}</resources>`));
|
|
2342
2504
|
}
|
|
2343
2505
|
//#endregion
|
|
2344
2506
|
//#region src/add-layers.ts
|
|
2345
2507
|
function applyDependencyOnlyLayer(root, layerName) {
|
|
2346
|
-
renderTemplate(path$
|
|
2508
|
+
renderTemplate(path$5.join(templatesRoot, "layers", layerName, "package.json.fragment.json"), path$5.join(root, "package.json.fragment.json"));
|
|
2347
2509
|
}
|
|
2348
2510
|
function applyExpoModulesLayer(root) {
|
|
2349
2511
|
applyDependencyOnlyLayer(root, "expo-modules");
|
|
2350
|
-
if (fs$
|
|
2351
|
-
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"))) {
|
|
2352
2514
|
applyExpoModulesInfoPlist(root);
|
|
2353
2515
|
applyExpoModulesPodfilePlatform(root);
|
|
2354
2516
|
applyExpoModulesPodfileAutolinking(root);
|
|
@@ -2365,14 +2527,14 @@ function applySplashScreenLayer(root, framework) {
|
|
|
2365
2527
|
applyBootsplashLogo(root, templatesRoot, framework);
|
|
2366
2528
|
}
|
|
2367
2529
|
async function applyTestingLayer(root, framework, confirmOverwrite) {
|
|
2368
|
-
const testingLayerRoot = path$
|
|
2530
|
+
const testingLayerRoot = path$5.join(templatesRoot, "layers", "testing");
|
|
2369
2531
|
const fragmentName = framework === "angular" ? "angular" : "base";
|
|
2370
|
-
renderTemplate(path$
|
|
2371
|
-
const detoxConfigPath = path$
|
|
2372
|
-
const e2eDirPath = path$
|
|
2373
|
-
if ((fs$
|
|
2374
|
-
renderTemplate(path$
|
|
2375
|
-
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 });
|
|
2376
2538
|
return true;
|
|
2377
2539
|
}
|
|
2378
2540
|
async function addLayersToApp(options) {
|
|
@@ -2447,105 +2609,114 @@ function getCommand(packageManager, script) {
|
|
|
2447
2609
|
return packageManager === "npm" ? `npm run ${script}` : `${packageManager} ${script}`;
|
|
2448
2610
|
}
|
|
2449
2611
|
//#endregion
|
|
2450
|
-
//#region src/
|
|
2451
|
-
var
|
|
2452
|
-
const fs = __require("node:fs");
|
|
2453
|
-
const path = __require("node:path");
|
|
2454
|
-
const
|
|
2455
|
-
const END_TAG = "SYMBIOTE-EXPO-LINK:END";
|
|
2456
|
-
const REGION_NOTE = "generated by symbiote-expo-link - do not edit, regenerated on every install";
|
|
2457
|
-
const REACT_ANDROID_ANCHOR = "implementation(\"com.facebook.react:react-android\")";
|
|
2458
|
-
const MODULES_MAP_ANCHOR = "override fun getModulesMap(): Map<Class<out Module>, String?> = mapOf(";
|
|
2459
|
-
const MODULES_PROVIDER_CLASS_ANCHOR = ": ModulesProvider {";
|
|
2460
|
-
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"];
|
|
2461
2617
|
function isDebug() {
|
|
2462
|
-
return
|
|
2618
|
+
return DEBUG_ENV_VALUES.includes(process.env.DEBUG) || globalThis.__SYMBIOTE_DEBUG__ === true;
|
|
2463
2619
|
}
|
|
2464
2620
|
function dlog(...args) {
|
|
2465
|
-
if (isDebug())
|
|
2621
|
+
if (isDebug()) process.stderr.write(`[symbiote-expo-link] ${args.join(" ")}\n`);
|
|
2466
2622
|
}
|
|
2467
2623
|
function warn(message) {
|
|
2468
2624
|
console.warn(`[symbiote-expo-link] ${message}`);
|
|
2469
2625
|
}
|
|
2470
2626
|
function writeFileAtomic(filePath, content) {
|
|
2471
2627
|
const tmpPath = `${filePath}.symbiote-expo-link.${process.pid}.tmp`;
|
|
2472
|
-
fs.writeFileSync(tmpPath, content);
|
|
2473
|
-
fs.renameSync(tmpPath, filePath);
|
|
2628
|
+
fs$5.writeFileSync(tmpPath, content);
|
|
2629
|
+
fs$5.renameSync(tmpPath, filePath);
|
|
2474
2630
|
}
|
|
2475
|
-
function
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
if (findInfoPlistFile(candidate)) return candidate;
|
|
2480
|
-
}
|
|
2481
|
-
return null;
|
|
2631
|
+
function applyIfChanged(filePath, content, nextContent) {
|
|
2632
|
+
if (nextContent === null || nextContent === content) return;
|
|
2633
|
+
writeFileAtomic(filePath, nextContent);
|
|
2634
|
+
dlog(`updated ${filePath}`);
|
|
2482
2635
|
}
|
|
2483
|
-
function
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
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];
|
|
2488
2642
|
while (stack.length > 0) {
|
|
2489
2643
|
const dir = stack.pop();
|
|
2490
|
-
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
2491
|
-
if (skip.test(entry.name)) continue;
|
|
2492
|
-
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);
|
|
2493
2647
|
if (entry.isDirectory()) stack.push(full);
|
|
2494
|
-
else if (entry.name ===
|
|
2648
|
+
else if (entry.name === fileName) return full;
|
|
2495
2649
|
}
|
|
2496
2650
|
}
|
|
2497
2651
|
return null;
|
|
2498
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
|
+
}
|
|
2499
2682
|
function findMainApplicationFile(appRoot) {
|
|
2500
|
-
const javaRoot = path.join(appRoot,
|
|
2501
|
-
|
|
2502
|
-
const stack = [javaRoot];
|
|
2503
|
-
while (stack.length > 0) {
|
|
2504
|
-
const dir = stack.pop();
|
|
2505
|
-
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
2506
|
-
const full = path.join(dir, entry.name);
|
|
2507
|
-
if (entry.isDirectory()) stack.push(full);
|
|
2508
|
-
else if (entry.name === "MainApplication.kt") return full;
|
|
2509
|
-
}
|
|
2510
|
-
}
|
|
2511
|
-
return null;
|
|
2683
|
+
const javaRoot = path$3.join(appRoot, ANDROID_DIR, "app", "src", "main", "java");
|
|
2684
|
+
return findFileInTree(javaRoot, MAIN_APPLICATION_FILE);
|
|
2512
2685
|
}
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
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.`);
|
|
2523
2714
|
return null;
|
|
2524
2715
|
}
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
warn(`no node_modules in ${appRoot}, nothing to link`);
|
|
2530
|
-
return [];
|
|
2531
|
-
}
|
|
2532
|
-
const found = [];
|
|
2533
|
-
for (const entry of fs.readdirSync(modulesRoot, { withFileTypes: true })) {
|
|
2534
|
-
if (entry.name.startsWith(".")) continue;
|
|
2535
|
-
if (entry.name.startsWith("@")) {
|
|
2536
|
-
const scopeDir = path.join(modulesRoot, entry.name);
|
|
2537
|
-
for (const scoped of fs.readdirSync(scopeDir, { withFileTypes: true })) {
|
|
2538
|
-
const entryFound = readManifest(path.join(scopeDir, scoped.name), `${entry.name}/${scoped.name}`);
|
|
2539
|
-
if (entryFound) found.push(entryFound);
|
|
2540
|
-
}
|
|
2541
|
-
continue;
|
|
2542
|
-
}
|
|
2543
|
-
const entryFound = readManifest(path.join(modulesRoot, entry.name), entry.name);
|
|
2544
|
-
if (entryFound) found.push(entryFound);
|
|
2545
|
-
}
|
|
2546
|
-
found.sort((a, b) => a.packageName < b.packageName ? -1 : a.packageName > b.packageName ? 1 : 0);
|
|
2547
|
-
dlog(`found ${found.length} linkable package(s): ${found.map((f) => f.packageName).join(", ")}`);
|
|
2548
|
-
return found;
|
|
2716
|
+
return {
|
|
2717
|
+
begin,
|
|
2718
|
+
end
|
|
2719
|
+
};
|
|
2549
2720
|
}
|
|
2550
2721
|
function writeRegion({ filePath, content, name, indent, bodyLines, findAnchorIndex, anchorLabel }) {
|
|
2551
2722
|
const lines = content.split("\n");
|
|
@@ -2554,14 +2725,10 @@ var import_src = (/* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
2554
2725
|
...bodyLines.map((line) => `${indent}${line}`),
|
|
2555
2726
|
`${indent}// ${END_TAG} ${name}`
|
|
2556
2727
|
];
|
|
2557
|
-
const
|
|
2558
|
-
if (
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
warn(`${filePath} has "${BEGIN_TAG} ${name}" but no matching "${END_TAG} ${name}". Restore the end marker (or delete the whole block) and reinstall.`);
|
|
2562
|
-
return null;
|
|
2563
|
-
}
|
|
2564
|
-
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);
|
|
2565
2732
|
return lines.join("\n");
|
|
2566
2733
|
}
|
|
2567
2734
|
const anchorIndex = findAnchorIndex(lines);
|
|
@@ -2572,22 +2739,23 @@ var import_src = (/* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
2572
2739
|
lines.splice(anchorIndex + 1, 0, ...block);
|
|
2573
2740
|
return lines.join("\n");
|
|
2574
2741
|
}
|
|
2575
|
-
function
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
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 : []);
|
|
2579
2747
|
}
|
|
2580
2748
|
function patchBuildGradle(appRoot, entries) {
|
|
2581
|
-
const gradlePath =
|
|
2582
|
-
if (!fs.existsSync(gradlePath)) {
|
|
2749
|
+
const gradlePath = buildGradlePath(appRoot);
|
|
2750
|
+
if (!fs$4.existsSync(gradlePath)) {
|
|
2583
2751
|
dlog("android/app/build.gradle not found, skipping");
|
|
2584
2752
|
return;
|
|
2585
2753
|
}
|
|
2586
2754
|
const primaryProjectNames = entries.filter((entry) => entry.manifest.android && entry.manifest.android.gradleProjectName).map((entry) => entry.manifest.android.gradleProjectName);
|
|
2587
2755
|
const serviceProjectNames = androidServicesOf(entries).map((service) => service.gradleProjectName);
|
|
2588
2756
|
const deps = [.../* @__PURE__ */ new Set([...primaryProjectNames, ...serviceProjectNames])].sort().map((name) => `implementation project(':${name}')`);
|
|
2589
|
-
const content = fs.readFileSync(gradlePath, "utf8");
|
|
2590
|
-
|
|
2757
|
+
const content = fs$4.readFileSync(gradlePath, "utf8");
|
|
2758
|
+
const next = writeRegion({
|
|
2591
2759
|
filePath: gradlePath,
|
|
2592
2760
|
content,
|
|
2593
2761
|
name: "DEPENDENCIES",
|
|
@@ -2595,41 +2763,102 @@ var import_src = (/* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
2595
2763
|
bodyLines: deps,
|
|
2596
2764
|
findAnchorIndex: (lines) => lines.findIndex((line) => line.includes(REACT_ANDROID_ANCHOR)),
|
|
2597
2765
|
anchorLabel: `no \`${REACT_ANDROID_ANCHOR}\` line to anchor below`
|
|
2598
|
-
})
|
|
2766
|
+
});
|
|
2767
|
+
applyIfChanged(gradlePath, content, next);
|
|
2599
2768
|
}
|
|
2600
|
-
function
|
|
2601
|
-
|
|
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");
|
|
2602
2776
|
}
|
|
2603
|
-
|
|
2604
|
-
|
|
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();
|
|
2605
2843
|
}
|
|
2606
2844
|
function patchMainApplication(appRoot, entries) {
|
|
2607
2845
|
const filePath = findMainApplicationFile(appRoot);
|
|
2608
2846
|
if (!filePath) {
|
|
2609
|
-
dlog(
|
|
2847
|
+
dlog(`${MAIN_APPLICATION_FILE} not found, skipping`);
|
|
2610
2848
|
return;
|
|
2611
2849
|
}
|
|
2612
2850
|
const modules = androidModulesOf(entries);
|
|
2613
2851
|
const services = androidServicesOf(entries);
|
|
2614
|
-
const imports = [.../* @__PURE__ */ new Set([
|
|
2615
|
-
...modules.map((module$1) => `import ${module$1.importPath}`),
|
|
2616
|
-
...services.map((service) => `import ${service.importPath}`),
|
|
2617
|
-
"import expo.modules.kotlin.services.Service"
|
|
2618
|
-
])].sort();
|
|
2619
2852
|
const mapEntries = [...new Set(modules.map((module$2) => `${module$2.className}::class.java to "${module$2.nativeName}",`))].sort();
|
|
2620
2853
|
const serviceEntries = [...new Set(services.map((service) => ` ${service.className}::class.java,`))].sort();
|
|
2621
|
-
const content = fs.readFileSync(filePath, "utf8");
|
|
2854
|
+
const content = fs$4.readFileSync(filePath, "utf8");
|
|
2622
2855
|
const withImports = writeRegion({
|
|
2623
2856
|
filePath,
|
|
2624
2857
|
content,
|
|
2625
2858
|
name: "IMPORTS",
|
|
2626
2859
|
indent: "",
|
|
2627
|
-
bodyLines:
|
|
2628
|
-
findAnchorIndex:
|
|
2629
|
-
let lastImport = -1;
|
|
2630
|
-
for (let i = 0; i < lines.length; i += 1) if (lines[i].trim().startsWith("import ")) lastImport = i;
|
|
2631
|
-
return lastImport;
|
|
2632
|
-
},
|
|
2860
|
+
bodyLines: mainApplicationImports(modules, services),
|
|
2861
|
+
findAnchorIndex: lastImportIndex,
|
|
2633
2862
|
anchorLabel: "no import statements to anchor below"
|
|
2634
2863
|
});
|
|
2635
2864
|
if (withImports === null) return;
|
|
@@ -2643,7 +2872,7 @@ var import_src = (/* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
2643
2872
|
anchorLabel: "no `getModulesMap()` declaration to anchor below"
|
|
2644
2873
|
});
|
|
2645
2874
|
if (withModules === null) return;
|
|
2646
|
-
|
|
2875
|
+
const next = writeRegion({
|
|
2647
2876
|
filePath,
|
|
2648
2877
|
content: withModules,
|
|
2649
2878
|
name: "SERVICES",
|
|
@@ -2655,31 +2884,45 @@ var import_src = (/* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
2655
2884
|
],
|
|
2656
2885
|
findAnchorIndex: (lines) => lines.findIndex((line) => line.includes(MODULES_PROVIDER_CLASS_ANCHOR)),
|
|
2657
2886
|
anchorLabel: "no `ExpoModulesProvider : ModulesProvider` class declaration to anchor below"
|
|
2658
|
-
})
|
|
2659
|
-
|
|
2660
|
-
function escapeXml(value) {
|
|
2661
|
-
return String(value).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
2887
|
+
});
|
|
2888
|
+
applyIfChanged(filePath, content, next);
|
|
2662
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";
|
|
2663
2904
|
function escapeXmlAttribute(value) {
|
|
2664
2905
|
return escapeXml(value).replace(/"/g, """);
|
|
2665
2906
|
}
|
|
2666
|
-
function
|
|
2667
|
-
const match = new RegExp(`<${tagName}(?=[\\s>/])`).exec(content);
|
|
2668
|
-
if (!match) return null;
|
|
2907
|
+
function scanTagEnd(content, from) {
|
|
2669
2908
|
let quote = null;
|
|
2670
|
-
for (let i =
|
|
2909
|
+
for (let i = from; i < content.length; i += 1) {
|
|
2671
2910
|
const char = content[i];
|
|
2672
2911
|
if (quote !== null) {
|
|
2673
2912
|
if (char === quote) quote = null;
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
if (char === "\"" || char === "'") quote = char;
|
|
2677
|
-
else if (char === ">") return {
|
|
2678
|
-
start: match.index,
|
|
2679
|
-
end: i
|
|
2680
|
-
};
|
|
2913
|
+
} else if (char === "\"" || char === "'") quote = char;
|
|
2914
|
+
else if (char === ">") return i;
|
|
2681
2915
|
}
|
|
2682
|
-
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
|
+
};
|
|
2683
2926
|
}
|
|
2684
2927
|
function findApplicationTag(content) {
|
|
2685
2928
|
return findTag(content, "application");
|
|
@@ -2689,27 +2932,22 @@ var import_src = (/* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
2689
2932
|
const re = new RegExp(`<${tagName}(?=[\\s>/])`, "g");
|
|
2690
2933
|
let match = re.exec(content);
|
|
2691
2934
|
while (match) {
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
if (char === "\"" || char === "'") quote = char;
|
|
2700
|
-
else if (char === ">") {
|
|
2701
|
-
tags.push({
|
|
2702
|
-
start: match.index,
|
|
2703
|
-
end: i
|
|
2704
|
-
});
|
|
2705
|
-
re.lastIndex = i + 1;
|
|
2706
|
-
break;
|
|
2707
|
-
}
|
|
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;
|
|
2708
2942
|
}
|
|
2709
2943
|
match = re.exec(content);
|
|
2710
2944
|
}
|
|
2711
2945
|
return tags;
|
|
2712
2946
|
}
|
|
2947
|
+
function readXmlAttribute(tagText, name) {
|
|
2948
|
+
const match = new RegExp(`\\s${name}\\s*=\\s*"([^"]*)"`).exec(tagText);
|
|
2949
|
+
return match ? match[1] : null;
|
|
2950
|
+
}
|
|
2713
2951
|
function findMainActivityTag(content) {
|
|
2714
2952
|
for (const tag of findAllTags(content, "activity")) {
|
|
2715
2953
|
const name = readXmlAttribute(content.slice(tag.start, tag.end), "android:name");
|
|
@@ -2717,26 +2955,48 @@ var import_src = (/* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
2717
2955
|
}
|
|
2718
2956
|
return null;
|
|
2719
2957
|
}
|
|
2720
|
-
function
|
|
2721
|
-
const
|
|
2722
|
-
return
|
|
2958
|
+
function attributeSeparator(tagText) {
|
|
2959
|
+
const lastNewline = tagText.lastIndexOf("\n");
|
|
2960
|
+
return lastNewline === -1 ? " " : `\n${/^[ \t]*/.exec(tagText.slice(lastNewline + 1))[0]}`;
|
|
2723
2961
|
}
|
|
2724
|
-
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) {
|
|
2725
2982
|
const wanted = /* @__PURE__ */ new Map();
|
|
2726
2983
|
for (const entry of entries) {
|
|
2727
2984
|
const attributes = entry.manifest.android && entry.manifest.android.manifestApplicationAttributes;
|
|
2728
|
-
if (!
|
|
2729
|
-
for (const [name, value] of Object.entries(attributes)) if (!wanted.has(name)) wanted.set(name, value);
|
|
2730
|
-
}
|
|
2731
|
-
if (wanted.size === 0) return;
|
|
2732
|
-
const manifestPath = path.join(appRoot, "android", "app", "src", "main", "AndroidManifest.xml");
|
|
2733
|
-
if (!fs.existsSync(manifestPath)) {
|
|
2734
|
-
dlog("AndroidManifest.xml not found, skipping application attributes");
|
|
2735
|
-
return;
|
|
2985
|
+
for (const [name, value] of Object.entries(attributes || {})) if (!wanted.has(name)) wanted.set(name, value);
|
|
2736
2986
|
}
|
|
2737
|
-
|
|
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;
|
|
2738
2998
|
let next = content;
|
|
2739
|
-
for (const [name, value] of
|
|
2999
|
+
for (const [name, value] of wanted) {
|
|
2740
3000
|
if (!/^[\w.:-]+$/.test(name)) {
|
|
2741
3001
|
warn(`skipping the manifest attribute "${name}": not a plain XML attribute name`);
|
|
2742
3002
|
continue;
|
|
@@ -2746,79 +3006,55 @@ var import_src = (/* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
2746
3006
|
warn(`${manifestPath} has no <application> element, skipping ${name}`);
|
|
2747
3007
|
return;
|
|
2748
3008
|
}
|
|
2749
|
-
const tagText = next.slice(tag.start, tag.end);
|
|
2750
3009
|
const escaped = escapeXmlAttribute(value);
|
|
2751
|
-
const existing = readXmlAttribute(
|
|
2752
|
-
if (existing
|
|
2753
|
-
|
|
2754
|
-
continue;
|
|
2755
|
-
}
|
|
2756
|
-
const lastNewline = tagText.lastIndexOf("\n");
|
|
2757
|
-
const separator = lastNewline === -1 ? " " : `\n${/^[ \t]*/.exec(tagText.slice(lastNewline + 1))[0]}`;
|
|
2758
|
-
const insertAt = next[tag.end - 1] === "/" ? tag.end - 1 : tag.end;
|
|
2759
|
-
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);
|
|
2760
3013
|
}
|
|
2761
3014
|
applyIfChanged(manifestPath, content, next);
|
|
2762
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
|
+
}
|
|
2763
3030
|
function patchMainActivityConfigChanges(appRoot, entries) {
|
|
2764
|
-
const wanted =
|
|
2765
|
-
for (const entry of entries) {
|
|
2766
|
-
const values = entry.manifest.android && entry.manifest.android.mainActivityConfigChanges;
|
|
2767
|
-
if (!values) continue;
|
|
2768
|
-
for (const value of values) wanted.add(value);
|
|
2769
|
-
}
|
|
3031
|
+
const wanted = new Set(androidListOf(entries, "mainActivityConfigChanges"));
|
|
2770
3032
|
if (wanted.size === 0) return;
|
|
2771
|
-
const
|
|
2772
|
-
if (!
|
|
2773
|
-
|
|
2774
|
-
return;
|
|
2775
|
-
}
|
|
2776
|
-
const content = fs.readFileSync(manifestPath, "utf8");
|
|
3033
|
+
const loaded = loadAndroidManifest(appRoot, "MainActivity configChanges");
|
|
3034
|
+
if (!loaded) return;
|
|
3035
|
+
const { manifestPath, content } = loaded;
|
|
2777
3036
|
const tag = findMainActivityTag(content);
|
|
2778
3037
|
if (!tag) {
|
|
2779
3038
|
warn(`${manifestPath} has no MainActivity <activity> element, skipping configChanges`);
|
|
2780
3039
|
return;
|
|
2781
3040
|
}
|
|
2782
|
-
const
|
|
2783
|
-
const existingValue = readXmlAttribute(tagText, "android:configChanges");
|
|
3041
|
+
const existingValue = readXmlAttribute(content.slice(tag.start, tag.end), CONFIG_CHANGES_ATTRIBUTE);
|
|
2784
3042
|
const existingTokens = existingValue ? existingValue.split("|").map((token) => token.trim()).filter(Boolean) : [];
|
|
2785
|
-
const
|
|
2786
|
-
const missing = [...wanted].filter((token) => !existingSet.has(token)).sort();
|
|
3043
|
+
const missing = [...wanted].filter((token) => !existingTokens.includes(token)).sort();
|
|
2787
3044
|
if (missing.length === 0) return;
|
|
2788
3045
|
const merged = [...existingTokens, ...missing].join("|");
|
|
2789
|
-
|
|
2790
|
-
if (existingValue !== null) {
|
|
2791
|
-
const valueMatch = /android:configChanges\s*=\s*"/.exec(tagText);
|
|
2792
|
-
const valueStart = valueMatch.index + valueMatch[0].length;
|
|
2793
|
-
const valueEnd = tagText.indexOf("\"", valueStart);
|
|
2794
|
-
const nextTagText = tagText.slice(0, valueStart) + merged + tagText.slice(valueEnd);
|
|
2795
|
-
next = content.slice(0, tag.start) + nextTagText + content.slice(tag.end);
|
|
2796
|
-
} else {
|
|
2797
|
-
const lastNewline = tagText.lastIndexOf("\n");
|
|
2798
|
-
const separator = lastNewline === -1 ? " " : `\n${/^[ \t]*/.exec(tagText.slice(lastNewline + 1))[0]}`;
|
|
2799
|
-
const selfClosing = content[tag.end - 1] === "/";
|
|
2800
|
-
const insertAt = selfClosing ? tag.end - 1 : tag.end;
|
|
2801
|
-
next = `${content.slice(0, insertAt).replace(/[ \t]+$/, "")}${separator}android:configChanges="${merged}"${selfClosing ? " " : ""}${content.slice(insertAt)}`;
|
|
2802
|
-
}
|
|
3046
|
+
const next = existingValue !== null ? replaceConfigChangesValue(content, tag, merged) : addConfigChangesAttribute(content, tag, merged);
|
|
2803
3047
|
applyIfChanged(manifestPath, content, next);
|
|
2804
3048
|
}
|
|
2805
3049
|
function readManifestPermissions(content) {
|
|
2806
3050
|
return new Set([...content.matchAll(/<uses-permission\s[^>]*android:name="([^"]+)"/g)].map((match) => match[1]));
|
|
2807
3051
|
}
|
|
2808
3052
|
function patchAndroidManifestPermissions(appRoot, entries) {
|
|
2809
|
-
const wanted =
|
|
2810
|
-
for (const entry of entries) {
|
|
2811
|
-
const permissions = entry.manifest.android && entry.manifest.android.manifestPermissions;
|
|
2812
|
-
if (!permissions) continue;
|
|
2813
|
-
for (const permission of permissions) wanted.add(permission);
|
|
2814
|
-
}
|
|
3053
|
+
const wanted = new Set(androidListOf(entries, "manifestPermissions"));
|
|
2815
3054
|
if (wanted.size === 0) return;
|
|
2816
|
-
const
|
|
2817
|
-
if (!
|
|
2818
|
-
|
|
2819
|
-
return;
|
|
2820
|
-
}
|
|
2821
|
-
const content = fs.readFileSync(manifestPath, "utf8");
|
|
3055
|
+
const loaded = loadAndroidManifest(appRoot, "permissions");
|
|
3056
|
+
if (!loaded) return;
|
|
3057
|
+
const { manifestPath, content } = loaded;
|
|
2822
3058
|
const existing = readManifestPermissions(content);
|
|
2823
3059
|
const missing = [...wanted].filter((permission) => !existing.has(permission)).sort();
|
|
2824
3060
|
if (missing.length === 0) return;
|
|
@@ -2841,21 +3077,13 @@ var import_src = (/* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
2841
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>`;
|
|
2842
3078
|
}
|
|
2843
3079
|
function patchAndroidManifestServices(appRoot, entries) {
|
|
2844
|
-
const
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
}
|
|
2850
|
-
if (wanted.size === 0) return;
|
|
2851
|
-
const manifestPath = path.join(appRoot, "android", "app", "src", "main", "AndroidManifest.xml");
|
|
2852
|
-
if (!fs.existsSync(manifestPath)) {
|
|
2853
|
-
dlog("AndroidManifest.xml not found, skipping services");
|
|
2854
|
-
return;
|
|
2855
|
-
}
|
|
2856
|
-
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;
|
|
2857
3085
|
const existing = readManifestServiceNames(content);
|
|
2858
|
-
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);
|
|
2859
3087
|
if (missing.length === 0) return;
|
|
2860
3088
|
const tag = findApplicationTag(content);
|
|
2861
3089
|
if (!tag) {
|
|
@@ -2867,50 +3095,130 @@ var import_src = (/* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
2867
3095
|
return;
|
|
2868
3096
|
}
|
|
2869
3097
|
const lastNewline = content.lastIndexOf("\n", tag.start);
|
|
2870
|
-
const
|
|
2871
|
-
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");
|
|
2872
3100
|
const insertAt = tag.end + 1;
|
|
2873
3101
|
applyIfChanged(manifestPath, content, `${content.slice(0, insertAt)}\n${blocks}${content.slice(insertAt)}`);
|
|
2874
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
|
+
}
|
|
2875
3216
|
function readPlistString(content, key) {
|
|
2876
3217
|
const keyIndex = content.indexOf(`<key>${key}</key>`);
|
|
2877
3218
|
if (keyIndex === -1) return null;
|
|
2878
3219
|
const match = /<string>([\s\S]*?)<\/string>/.exec(content.slice(keyIndex));
|
|
2879
3220
|
return match ? match[1] : null;
|
|
2880
3221
|
}
|
|
2881
|
-
function patchInfoPlist(appRoot, entries) {
|
|
2882
|
-
const wanted = /* @__PURE__ */ new Map();
|
|
2883
|
-
for (const entry of entries) {
|
|
2884
|
-
const keys = entry.manifest.ios && entry.manifest.ios.infoPlistKeys;
|
|
2885
|
-
if (!keys) continue;
|
|
2886
|
-
for (const [key, description] of Object.entries(keys)) if (!wanted.has(key)) wanted.set(key, description);
|
|
2887
|
-
}
|
|
2888
|
-
if (wanted.size === 0) return;
|
|
2889
|
-
const plistPath = findInfoPlistFile(appRoot);
|
|
2890
|
-
if (!plistPath) {
|
|
2891
|
-
dlog("Info.plist not found, skipping iOS permission strings");
|
|
2892
|
-
return;
|
|
2893
|
-
}
|
|
2894
|
-
const content = fs.readFileSync(plistPath, "utf8");
|
|
2895
|
-
let next = content;
|
|
2896
|
-
for (const [key, description] of [...wanted].sort((a, b) => a[0] < b[0] ? -1 : 1)) {
|
|
2897
|
-
const escaped = escapeXml(description);
|
|
2898
|
-
const existing = readPlistString(next, key);
|
|
2899
|
-
if (existing !== null) {
|
|
2900
|
-
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.`);
|
|
2901
|
-
continue;
|
|
2902
|
-
}
|
|
2903
|
-
const plistCloseIndex = next.lastIndexOf("</plist>");
|
|
2904
|
-
const dictCloseIndex = next.lastIndexOf("</dict>", plistCloseIndex === -1 ? void 0 : plistCloseIndex);
|
|
2905
|
-
if (dictCloseIndex === -1) {
|
|
2906
|
-
warn(`${plistPath} has no closing </dict>, skipping ${key}`);
|
|
2907
|
-
continue;
|
|
2908
|
-
}
|
|
2909
|
-
const block = `\t<key>${key}</key>\n\t<string>${escaped}</string>\n`;
|
|
2910
|
-
next = next.slice(0, dictCloseIndex) + block + next.slice(dictCloseIndex);
|
|
2911
|
-
}
|
|
2912
|
-
applyIfChanged(plistPath, content, next);
|
|
2913
|
-
}
|
|
2914
3222
|
function readPlistArray(content, key) {
|
|
2915
3223
|
const keyTag = `<key>${key}</key>`;
|
|
2916
3224
|
const keyIndex = content.indexOf(keyTag);
|
|
@@ -2923,44 +3231,151 @@ var import_src = (/* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
2923
3231
|
items: [...content.slice(arrayOpenIndex + 7, arrayCloseIndex).matchAll(/<string>([\s\S]*?)<\/string>/g)].map((match) => match[1])
|
|
2924
3232
|
};
|
|
2925
3233
|
}
|
|
2926
|
-
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) {
|
|
2927
3277
|
const wanted = /* @__PURE__ */ new Map();
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
|
|
2933
|
-
|
|
2934
|
-
|
|
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;
|
|
2935
3294
|
}
|
|
2936
|
-
|
|
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;
|
|
2937
3309
|
const plistPath = findInfoPlistFile(appRoot);
|
|
2938
3310
|
if (!plistPath) {
|
|
2939
|
-
dlog(
|
|
3311
|
+
dlog(`${INFO_PLIST} not found, skipping iOS entitlements`);
|
|
2940
3312
|
return;
|
|
2941
3313
|
}
|
|
2942
|
-
const
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
|
|
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;
|
|
3345
|
+
}
|
|
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;
|
|
2962
3359
|
}
|
|
2963
|
-
|
|
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;
|
|
2964
3379
|
}
|
|
2965
3380
|
function linkApp(explicitAppRoot) {
|
|
2966
3381
|
const appRoot = explicitAppRoot || findAppRoot();
|
|
@@ -2970,6 +3385,7 @@ var import_src = (/* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
2970
3385
|
}
|
|
2971
3386
|
const entries = collectManifests(appRoot);
|
|
2972
3387
|
patchBuildGradle(appRoot, entries);
|
|
3388
|
+
patchRootBuildGradle(appRoot, entries);
|
|
2973
3389
|
patchMainApplication(appRoot, entries);
|
|
2974
3390
|
patchAndroidManifest(appRoot, entries);
|
|
2975
3391
|
patchAndroidManifestPermissions(appRoot, entries);
|
|
@@ -2977,6 +3393,8 @@ var import_src = (/* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
2977
3393
|
patchMainActivityConfigChanges(appRoot, entries);
|
|
2978
3394
|
patchInfoPlist(appRoot, entries);
|
|
2979
3395
|
patchInfoPlistArrays(appRoot, entries);
|
|
3396
|
+
patchInfoPlistBooleans(appRoot, entries);
|
|
3397
|
+
patchEntitlements(appRoot, entries);
|
|
2980
3398
|
}
|
|
2981
3399
|
module.exports = {
|
|
2982
3400
|
linkApp,
|
|
@@ -2985,13 +3403,16 @@ var import_src = (/* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
2985
3403
|
findInfoPlistFile,
|
|
2986
3404
|
findMainApplicationFile,
|
|
2987
3405
|
patchBuildGradle,
|
|
3406
|
+
patchRootBuildGradle,
|
|
2988
3407
|
patchMainApplication,
|
|
2989
3408
|
patchAndroidManifest,
|
|
2990
3409
|
patchAndroidManifestPermissions,
|
|
2991
3410
|
patchAndroidManifestServices,
|
|
2992
3411
|
patchMainActivityConfigChanges,
|
|
2993
3412
|
patchInfoPlist,
|
|
2994
|
-
patchInfoPlistArrays
|
|
3413
|
+
patchInfoPlistArrays,
|
|
3414
|
+
patchInfoPlistBooleans,
|
|
3415
|
+
patchEntitlements
|
|
2995
3416
|
};
|
|
2996
3417
|
})))();
|
|
2997
3418
|
function discoveredBundlesFromLayers(layers) {
|
|
@@ -3482,13 +3903,13 @@ function tryGitInit(root) {
|
|
|
3482
3903
|
async function runNew(parsed) {
|
|
3483
3904
|
intro("@symbiote-native/cli new");
|
|
3484
3905
|
const appName = await resolveAppName(parsed.appName);
|
|
3485
|
-
const targetDir = path$
|
|
3906
|
+
const targetDir = path$5.resolve(process.cwd(), appName);
|
|
3486
3907
|
if (!isEmptyDir(targetDir)) {
|
|
3487
3908
|
if (!await resolveOverwrite(targetDir, parsed.hasForce)) {
|
|
3488
3909
|
cancel("Cancelled.");
|
|
3489
3910
|
process.exit(1);
|
|
3490
3911
|
}
|
|
3491
|
-
fs$
|
|
3912
|
+
fs$6.rmSync(targetDir, {
|
|
3492
3913
|
recursive: true,
|
|
3493
3914
|
force: true
|
|
3494
3915
|
});
|
|
@@ -3514,7 +3935,7 @@ async function runNew(parsed) {
|
|
|
3514
3935
|
styling,
|
|
3515
3936
|
packageManager
|
|
3516
3937
|
});
|
|
3517
|
-
const hasGitDir = fs$
|
|
3938
|
+
const hasGitDir = fs$6.existsSync(path$5.join(root, ".git"));
|
|
3518
3939
|
const lines = [
|
|
3519
3940
|
`Scaffolded "${appName}" at ${root}.`,
|
|
3520
3941
|
"",
|
|
@@ -3545,7 +3966,7 @@ async function runNew(parsed) {
|
|
|
3545
3966
|
}
|
|
3546
3967
|
//#endregion
|
|
3547
3968
|
//#region package.json
|
|
3548
|
-
var version = "0.
|
|
3969
|
+
var version = "0.4.0";
|
|
3549
3970
|
//#endregion
|
|
3550
3971
|
//#region src/index.ts
|
|
3551
3972
|
function printHelp() {
|