@capacitor/cli 9.0.0-alpha.5 → 9.0.0-alpha.7

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.
Binary file
Binary file
Binary file
@@ -24,11 +24,14 @@ async function updateAndroid(config) {
24
24
  const capacitorPlugins = plugins.filter((p) => (0, plugin_1.getPluginType)(p, platform) === 0 /* PluginType.Core */);
25
25
  (0, plugin_1.printPlugins)(capacitorPlugins, 'android');
26
26
  const cordovaPlugins = plugins.filter((p) => (0, plugin_1.getPluginType)(p, platform) === 1 /* PluginType.Cordova */);
27
- const enableCordova = cordovaPlugins.length > 0;
27
+ const enableCordova = cordovaPlugins.length > 0 || config.app.forceCordova;
28
28
  await writePluginsJson(config, capacitorPlugins, enableCordova);
29
29
  await removePluginsNativeFiles(config);
30
30
  if (enableCordova) {
31
31
  log_1.logger.info('Found Cordova Plugins: Including Android Cordova Support');
32
+ if (config.app.forceCordova) {
33
+ log_1.logger.info('Cordova support installation has been forced');
34
+ }
32
35
  await copyPluginsNativeFiles(config, cordovaPlugins);
33
36
  }
34
37
  if (!(await (0, fs_extra_1.pathExists)(config.android.webDirAbs))) {
@@ -75,7 +78,7 @@ async function findAndroidPluginClassesInPlugin(plugin) {
75
78
  const srcFiles = await (0, fs_1.readdirp)(srcPath, {
76
79
  filter: (entry) => !entry.stats.isDirectory() && ['.java', '.kt'].includes((0, path_1.extname)(entry.path)),
77
80
  });
78
- const classRegex = /^@(?:CapacitorPlugin|NativePlugin)[\s\S]+?class ([\w]+)/gm;
81
+ const classRegex = /^@CapacitorPlugin[\s\S]+?class ([\w]+)/gm;
79
82
  const packageRegex = /^package ([\w.]+);?$/gm;
80
83
  debug('Searching %O source files in %O by %O regex', srcFiles.length, srcPath, classRegex);
81
84
  const entries = await Promise.all(srcFiles.map(async (srcFile) => {
@@ -107,7 +110,7 @@ async function installGradlePlugins(config, capacitorPlugins, cordovaPlugins) {
107
110
  (0, errors_1.fatal)(`Unable to find ${colors_1.default.strong('node_modules/@capacitor/android')}.\n` +
108
111
  `Are you sure ${colors_1.default.strong('@capacitor/android')} is installed?`);
109
112
  }
110
- const enableCordova = cordovaPlugins.length > 0;
113
+ const enableCordova = cordovaPlugins.length > 0 || config.app.forceCordova;
111
114
  const capacitorAndroidPath = (0, path_1.resolve)((0, path_1.dirname)(capacitorAndroidPackagePath), 'capacitor');
112
115
  const capacitorCordovaAndroidPath = (0, path_1.resolve)((0, path_1.dirname)(capacitorAndroidPackagePath), 'capacitor-cordova');
113
116
  const settingsPath = config.android.platformDirAbs;
@@ -206,10 +209,7 @@ if (hasProperty('postBuildExtras')) {
206
209
  }
207
210
  exports.installGradlePlugins = installGradlePlugins;
208
211
  async function handleCordovaPluginsGradle(config, cordovaPlugins) {
209
- var _a, _b, _c;
210
212
  const pluginsGradlePath = (0, path_1.join)(config.android.cordovaPluginsDirAbs, 'build.gradle');
211
- const kotlinNeeded = await kotlinNeededCheck(config, cordovaPlugins);
212
- const kotlinVersionString = (_c = (_b = (_a = config.app.extConfig.cordova) === null || _a === void 0 ? void 0 : _a.preferences) === null || _b === void 0 ? void 0 : _b.GradlePluginKotlinVersion) !== null && _c !== void 0 ? _c : '2.4.0';
213
213
  const frameworksArray = [];
214
214
  let prefsArray = [];
215
215
  const applyArray = [];
@@ -241,21 +241,10 @@ async function handleCordovaPluginsGradle(config, cordovaPlugins) {
241
241
  })
242
242
  .join('\n');
243
243
  frameworkString = await replaceFrameworkVariables(config, prefsArray, frameworkString);
244
- if (kotlinNeeded) {
245
- frameworkString += `\n implementation "androidx.core:core-ktx:$androidxCoreKTXVersion"`;
246
- frameworkString += `\n implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"`;
247
- }
248
244
  const applyString = applyArray.join('\n');
249
245
  let buildGradle = await (0, fs_extra_1.readFile)(pluginsGradlePath, { encoding: 'utf-8' });
250
246
  buildGradle = buildGradle.replace(/(SUB-PROJECT DEPENDENCIES START)[\s\S]*(\/\/ SUB-PROJECT DEPENDENCIES END)/, '$1\n' + frameworkString.concat('\n') + ' $2');
251
247
  buildGradle = buildGradle.replace(/(PLUGIN GRADLE EXTENSIONS START)[\s\S]*(\/\/ PLUGIN GRADLE EXTENSIONS END)/, '$1\n' + applyString.concat('\n') + '$2');
252
- if (kotlinNeeded) {
253
- buildGradle = buildGradle.replace(/(buildscript\s{\n(\t|\s{4})repositories\s{\n((\t{2}|\s{8}).+\n)+(\t|\s{4})}\n(\t|\s{4})dependencies\s{\n(\t{2}|\s{8}).+)\n((\t|\s{4})}\n}\n)/, `$1\n classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"\n$8`);
254
- buildGradle = buildGradle.replace(/(ext\s{)/, `$1\n androidxCoreKTXVersion = project.hasProperty('androidxCoreKTXVersion') ? rootProject.ext.androidxCoreKTXVersion : '1.8.0'`);
255
- buildGradle = buildGradle.replace(/(buildscript\s{)/, `$1\n ext.kotlin_version = project.hasProperty('kotlin_version') ? rootProject.ext.kotlin_version : '${kotlinVersionString}'`);
256
- buildGradle = buildGradle.replace(/(apply\splugin:\s'com\.android\.library')/, `$1\napply plugin: 'kotlin-android'`);
257
- buildGradle = buildGradle.replace(/(compileOptions\s{\n((\t{2}|\s{8}).+\n)+(\t|\s{4})})\n(})/, `$1\n sourceSets {\n main.java.srcDirs += 'src/main/kotlin'\n }\n$5`);
258
- }
259
248
  await (0, fs_extra_1.writeFile)(pluginsGradlePath, buildGradle);
260
249
  const cordovaVariables = `// DO NOT EDIT THIS FILE! IT IS GENERATED EACH TIME "capacitor update" IS RUN
261
250
  ext {
@@ -267,26 +256,6 @@ ext {
267
256
  await (0, fs_extra_1.writeFile)((0, path_1.join)(config.android.cordovaPluginsDirAbs, 'cordova.variables.gradle'), cordovaVariables);
268
257
  }
269
258
  exports.handleCordovaPluginsGradle = handleCordovaPluginsGradle;
270
- async function kotlinNeededCheck(config, cordovaPlugins) {
271
- var _a, _b;
272
- if (((_b = (_a = config.app.extConfig.cordova) === null || _a === void 0 ? void 0 : _a.preferences) === null || _b === void 0 ? void 0 : _b.GradlePluginKotlinEnabled) !== 'true') {
273
- for (const plugin of cordovaPlugins) {
274
- const androidPlatform = (0, plugin_1.getPluginPlatform)(plugin, platform);
275
- const sourceFiles = androidPlatform['source-file'];
276
- if (sourceFiles) {
277
- for (const srcFile of sourceFiles) {
278
- if (/^.*\.kt$/.test(srcFile['$'].src)) {
279
- return true;
280
- }
281
- }
282
- }
283
- }
284
- return false;
285
- }
286
- else {
287
- return true;
288
- }
289
- }
290
259
  async function copyPluginsNativeFiles(config, cordovaPlugins) {
291
260
  const pluginsPath = (0, path_1.join)(config.android.cordovaPluginsDirAbs, 'src', 'main');
292
261
  for (const p of cordovaPlugins) {
package/dist/config.js CHANGED
@@ -20,7 +20,7 @@ exports.CONFIG_FILE_NAME_TS = 'capacitor.config.ts';
20
20
  exports.CONFIG_FILE_NAME_JS = 'capacitor.config.js';
21
21
  exports.CONFIG_FILE_NAME_JSON = 'capacitor.config.json';
22
22
  async function loadConfig() {
23
- var _a, _b, _c, _d;
23
+ var _a, _b, _c, _d, _e;
24
24
  const appRootDir = process.cwd();
25
25
  const cliRootDir = (0, path_1.dirname)(__dirname);
26
26
  const conf = await loadExtConfig(appRootDir);
@@ -41,6 +41,7 @@ async function loadConfig() {
41
41
  const appId = (_a = conf.extConfig.appId) !== null && _a !== void 0 ? _a : '';
42
42
  const appName = (_b = conf.extConfig.appName) !== null && _b !== void 0 ? _b : '';
43
43
  const webDir = (_c = conf.extConfig.webDir) !== null && _c !== void 0 ? _c : 'www';
44
+ const forceCordova = (_d = conf.extConfig.forceCordovaInclusion) !== null && _d !== void 0 ? _d : false;
44
45
  const cli = await loadCLIConfig(cliRootDir);
45
46
  const config = {
46
47
  android: await loadAndroidConfig(appRootDir, conf.extConfig, cli),
@@ -53,7 +54,8 @@ async function loadConfig() {
53
54
  appName,
54
55
  webDir,
55
56
  webDirAbs: (0, path_1.resolve)(appRootDir, webDir),
56
- package: (_d = (await (0, fn_1.tryFn)(fs_extra_1.readJSON, (0, path_1.resolve)(appRootDir, 'package.json')))) !== null && _d !== void 0 ? _d : {
57
+ forceCordova,
58
+ package: (_e = (await (0, fn_1.tryFn)(fs_extra_1.readJSON, (0, path_1.resolve)(appRootDir, 'package.json')))) !== null && _e !== void 0 ? _e : {
57
59
  name: appName,
58
60
  version: '1.0.0',
59
61
  ...depsForNx,
@@ -87,7 +89,7 @@ async function loadExtConfigTS(rootDir, extConfigName, extConfigFilePath) {
87
89
  `To use ${colors_1.default.strong(extConfigName)} files, you must install TypeScript in your project, e.g. w/ ${colors_1.default.input('npm install -D typescript')}`);
88
90
  }
89
91
  const ts = require(tsPath); // eslint-disable-line @typescript-eslint/no-var-requires
90
- const extConfigObject = (0, node_1.requireTS)(ts, extConfigFilePath);
92
+ const extConfigObject = (await (0, node_1.requireTS)(ts, extConfigFilePath));
91
93
  const extConfig = extConfigObject.default ? await extConfigObject.default : extConfigObject;
92
94
  return {
93
95
  extConfigType: 'ts',
package/dist/cordova.js CHANGED
@@ -754,13 +754,26 @@ async function writeCordovaAndroidManifest(cordovaPlugins, config, platform, cle
754
754
  });
755
755
  });
756
756
  const cleartextString = 'android:usesCleartextTraffic="true"';
757
- const cleartextValue = (cleartext || ((_a = config.app.extConfig.server) === null || _a === void 0 ? void 0 : _a.cleartext)) && !applicationXMLAttributes.includes(cleartextString)
758
- ? cleartextString
757
+ const networkSecurityConfigValue = cleartext || ((_a = config.app.extConfig.server) === null || _a === void 0 ? void 0 : _a.cleartext) || applicationXMLAttributes.includes(cleartextString)
758
+ ? 'android:networkSecurityConfig="@xml/network_security_config"'
759
759
  : '';
760
+ const networkSecurityConfigDir = (0, path_1.join)(config.android.cordovaPluginsDirAbs, 'src', 'main', 'res', 'xml');
761
+ const networkSecurityConfigPath = (0, path_1.join)(networkSecurityConfigDir, 'network_security_config.xml');
762
+ if (networkSecurityConfigValue) {
763
+ await (0, fs_extra_1.ensureDir)(networkSecurityConfigDir);
764
+ await (0, fs_extra_1.writeFile)(networkSecurityConfigPath, `<?xml version="1.0" encoding="utf-8"?>
765
+ <network-security-config>
766
+ <base-config cleartextTrafficPermitted="true" />
767
+ </network-security-config>
768
+ `);
769
+ }
770
+ else if (await (0, fs_extra_1.pathExists)(networkSecurityConfigPath)) {
771
+ await (0, fs_extra_1.remove)(networkSecurityConfigPath);
772
+ }
760
773
  let content = `<?xml version='1.0' encoding='utf-8'?>
761
774
  <manifest xmlns:android="http://schemas.android.com/apk/res/android"
762
775
  xmlns:amazon="http://schemas.amazon.com/apk/res/android">
763
- <application ${applicationXMLAttributes.join('\n')} ${cleartextValue}>
776
+ <application ${applicationXMLAttributes.filter((attr) => !attr.includes('android:usesCleartextTraffic')).join('\n')} ${networkSecurityConfigValue}>
764
777
  ${applicationXMLEntries.join('\n')}
765
778
  </application>
766
779
  ${rootXMLEntries.join('\n')}
@@ -76,6 +76,14 @@ export interface CapacitorConfig {
76
76
  * @default true
77
77
  */
78
78
  initialFocus?: boolean;
79
+ /**
80
+ * If set, will include Cordova support libraries regardless
81
+ * of whether or not they are needed by plugins.
82
+ *
83
+ * @since 9.0.0
84
+ * @default false
85
+ */
86
+ forceCordovaInclusion?: boolean;
79
87
  android?: {
80
88
  /**
81
89
  * Specify a custom path to the native Android project.
@@ -520,6 +528,46 @@ export interface CapacitorConfig {
520
528
  packageOptions?: {
521
529
  [pluginId: string]: PackageOptions;
522
530
  };
531
+ /**
532
+ * Override which Capacitor Swift package the generated `Package.swift` depends on.
533
+ *
534
+ * By default the generated package pins the git tag matching the installed
535
+ * `@capacitor/ios` version, which is correct for both stable and pre-release versions
536
+ * and matches what CocoaPods resolves.
537
+ *
538
+ * Set this only when developing against an unreleased Capacitor, such as a local
539
+ * checkout or a feature branch. Exactly one of `path`, `branch`, `revision`, `exact`,
540
+ * or `from` must be set. `path` points at the repository root (the directory containing
541
+ * `Package.swift`) and may be relative to the app.
542
+ *
543
+ * The `CAPACITOR_IOS_PACKAGE` environment variable takes precedence over this setting,
544
+ * so a checkout can be redirected without editing committed configuration.
545
+ *
546
+ * Note that `branch` and `revision` builds are not reproducible and decouple the native
547
+ * code from the `@capacitor/core` JavaScript bridge it is versioned against.
548
+ *
549
+ * @since 9.0.0
550
+ * @example { "path": "../capacitor" }
551
+ * @example { "branch": "next" }
552
+ */
553
+ capacitorPackage?: {
554
+ /**
555
+ * Repository to fetch from. Ignored when `path` is set.
556
+ *
557
+ * @default 'https://github.com/ionic-team/capacitor'
558
+ */
559
+ url?: string;
560
+ /** Path to a local Capacitor repository root. */
561
+ path?: string;
562
+ /** Branch to track. */
563
+ branch?: string;
564
+ /** Exact commit to pin. */
565
+ revision?: string;
566
+ /** Exact version tag to pin. */
567
+ exact?: string;
568
+ /** Minimum version tag, allowing compatible upgrades. */
569
+ from?: string;
570
+ };
523
571
  };
524
572
  };
525
573
  };
@@ -709,13 +757,31 @@ export interface PluginsConfig {
709
757
  *
710
758
  * This option is only supported on Android.
711
759
  *
712
- * `css` = Injects CSS variables (`--safe-area-inset-*`) containing correct safe area inset values into the webview.
760
+ * `native` = (recommended) For older Chromium versions (< v140) this embeds the webview with padding and sets the `env(safe-area-inset-*)` variables to `0px`. For newer Chromium versions (>= v140) this makes sure the webview adheres to the `viewport-fit` meta tag. If set to `viewport-fit="cover"` this will make the webview edge-to-edge and the `env(safe-area-inset-*)` variables will contain the correct values. With those values you could set padding for example so make sure the webview is shown correctly.
761
+ *
762
+ * `css` = This is the same as `native`, but it also injects CSS variables (`--safe-area-inset-*`) containing correct safe area inset values into the webview.
763
+ *
764
+ * `disable` = (not recommended) Disable safe area insets handling completely.
765
+ * This shifts the responsibility from Capacitor to your own code to handle the insets.
766
+ * Be aware that this might result in a visually broken UI if your native app code and the content loaded into the webview do not correctly handle safe area insets.
713
767
  *
714
- * `disable` = Disable CSS variables injection.
768
+ * @default "native"
769
+ */
770
+ insetsHandling?: 'native' | 'css' | 'disable';
771
+ /**
772
+ * Set an initial value for the to be detected `viewport-fit=` meta tag value.
773
+ * For most apps that support edge-to-edge this value will eventually be `cover`.
774
+ * Therefore you might want to set this value to `cover` to help prevent layout jumps and glitches.
775
+ * If you know the value to be `cover` initially, you can set it here.
776
+ * The value will always end up correctly, no matter what you set here,
777
+ * as long as `insetsHandling` is set to `native` or `css`.
778
+ * It only exists to help prevent layout jumps and glitches.
779
+ *
780
+ * This option is only supported on Android.
715
781
  *
716
- * @default "css"
782
+ * @default undefined
717
783
  */
718
- insetsHandling?: 'css' | 'disable';
784
+ initialViewportFitValueHint?: 'auto' | 'contain' | 'cover';
719
785
  /**
720
786
  * The style of the text and icons of the system bars.
721
787
  *
@@ -13,6 +13,7 @@ const log_1 = require("../log");
13
13
  const plugin_1 = require("../plugin");
14
14
  const copy_1 = require("../tasks/copy");
15
15
  const migrate_1 = require("../tasks/migrate");
16
+ const capacitor_package_1 = require("../util/capacitor-package");
16
17
  const cordova_ios_1 = require("../util/cordova-ios");
17
18
  const fs_1 = require("../util/fs");
18
19
  const iosplugin_1 = require("../util/iosplugin");
@@ -34,9 +35,12 @@ async function updatePluginFiles(config, plugins, deployment) {
34
35
  var _a;
35
36
  await (0, cordova_ios_1.removePluginsNativeFiles)(config);
36
37
  const cordovaPlugins = plugins.filter((p) => (0, plugin_1.getPluginType)(p, platform) === 1 /* PluginType.Cordova */);
37
- const enableCordova = cordovaPlugins.length > 0;
38
+ const enableCordova = cordovaPlugins.length > 0 || config.app.forceCordova;
38
39
  if (enableCordova) {
39
40
  log_1.logger.info('Found Cordova Plugins: Including iOS Cordova Support');
41
+ if (config.app.forceCordova) {
42
+ log_1.logger.info('Cordova support installation has been forced');
43
+ }
40
44
  await (0, cordova_ios_1.copyPluginsNativeFiles)(config, cordovaPlugins);
41
45
  }
42
46
  if (!(await (0, fs_extra_1.pathExists)(await config.ios.webDirAbs))) {
@@ -49,20 +53,30 @@ async function updatePluginFiles(config, plugins, deployment) {
49
53
  await (0, cordova_ios_1.generateCordovaPackageFiles)(cordovaPlugins, config);
50
54
  }
51
55
  const validSPMPackages = await (0, spm_1.checkPluginsForPackageSwift)(config, plugins);
56
+ const iosPlatformVersion = await (0, common_1.getCapacitorPackageVersion)(config, config.ios.name);
57
+ const majorCapVersion = (0, semver_1.major)(iosPlatformVersion);
58
+ const capacitorPackage = await (0, capacitor_package_1.resolveCapacitorPackage)(config, 'from');
52
59
  await Promise.all(validSPMPackages.map(async (plugin) => {
53
- var _a;
54
- const iosPlatformVersion = await (0, common_1.getCapacitorPackageVersion)(config, config.ios.name);
55
60
  const packageSwiftPath = (0, path_1.join)(plugin.rootPath, 'Package.swift');
56
61
  let content = await (0, fs_extra_1.readFile)(packageSwiftPath, { encoding: 'utf-8' });
57
- const regex = new RegExp('url:\\s*"https://github.com/ionic-team/capacitor-swift-pm\\.git",\\s*from:\\s*"([^"]+)"');
58
- const version = (_a = content.match(regex)) === null || _a === void 0 ? void 0 : _a[1];
59
- const majorCapVersion = (0, semver_1.major)(iosPlatformVersion);
62
+ const version = (0, capacitor_package_1.findCapacitorDependencyVersion)(content);
63
+ if (version && (0, semver_1.major)(version) != majorCapVersion) {
64
+ log_1.logger.warn(`${plugin.id} is built for Capacitor ${(0, semver_1.major)(version)}, it might cause issues`);
65
+ }
66
+ if (capacitorPackage.sourceBased) {
67
+ // The plugin has to resolve to the same package identity as the app, or SPM ends up with
68
+ // two packages that both vend a Capacitor product.
69
+ const rewritten = (0, capacitor_package_1.rewriteCapacitorDependency)(content, capacitorPackage, plugin.rootPath);
70
+ if (rewritten !== content) {
71
+ await (0, fs_extra_1.writeFile)(packageSwiftPath, rewritten);
72
+ }
73
+ return;
74
+ }
60
75
  if (version && (0, semver_1.major)(version) != majorCapVersion) {
61
76
  const preCapVersion = (0, semver_1.prerelease)(iosPlatformVersion);
62
77
  const forceVersion = preCapVersion ? iosPlatformVersion : `${majorCapVersion}.0.0`;
63
78
  content = (0, migrate_1.setAllStringIn)(content, `url: "https://github.com/ionic-team/capacitor-swift-pm.git",`, `)`, ` from: "${forceVersion}"`);
64
79
  await (0, fs_extra_1.writeFile)(packageSwiftPath, content);
65
- log_1.logger.warn(`${plugin.id} is built for Capacitor ${(0, semver_1.major)(version)}, it might cause issues`);
66
80
  }
67
81
  }));
68
82
  await (0, spm_1.generatePackageFile)(config, validSPMPackages.concat(cordovaPlugins));
@@ -0,0 +1,158 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.__testables = exports.migrateToEdgeToEdge = void 0;
4
+ const fs_extra_1 = require("fs-extra");
5
+ const path_1 = require("path");
6
+ const log_1 = require("../log");
7
+ const fs_1 = require("../util/fs");
8
+ const JAVA = {
9
+ imports: ['import android.os.Bundle;', 'import androidx.activity.EdgeToEdge;'],
10
+ enableCall: 'EdgeToEdge.enable(this);',
11
+ onCreate: `
12
+ @Override
13
+ protected void onCreate(Bundle savedInstanceState) {
14
+ super.onCreate(savedInstanceState);
15
+ EdgeToEdge.enable(this);
16
+ }
17
+ `,
18
+ };
19
+ const KOTLIN = {
20
+ imports: ['import android.os.Bundle', 'import androidx.activity.enableEdgeToEdge'],
21
+ enableCall: 'enableEdgeToEdge()',
22
+ onCreate: `
23
+ override fun onCreate(savedInstanceState: Bundle?) {
24
+ super.onCreate(savedInstanceState)
25
+ enableEdgeToEdge()
26
+ }
27
+ `,
28
+ };
29
+ async function migrateToEdgeToEdge(config) {
30
+ var _a, _b;
31
+ if (((_b = (_a = config.app.extConfig.plugins) === null || _a === void 0 ? void 0 : _a.SystemBars) === null || _b === void 0 ? void 0 : _b.insetsHandling) === 'disable') {
32
+ log_1.logger.info(`Edge-to-edge migration: SystemBars insetsHandling is 'disable', skipping.`);
33
+ return;
34
+ }
35
+ const activities = await findBridgeActivities(config);
36
+ if (activities.length === 0) {
37
+ log_1.logger.warn(`Edge-to-edge migration: no Activity extending BridgeActivity found. ${manualInstructions(JAVA.enableCall)}`);
38
+ return;
39
+ }
40
+ if (activities.length > 1) {
41
+ log_1.logger.warn(`Edge-to-edge migration: found more than one Activity extending BridgeActivity (${activities.join(', ')}), ` +
42
+ `skipping. ${manualInstructions(JAVA.enableCall)}`);
43
+ return;
44
+ }
45
+ const path = activities[0];
46
+ const language = path.endsWith('.kt') ? KOTLIN : JAVA;
47
+ const source = (0, fs_extra_1.readFileSync)(path, 'utf-8');
48
+ if (hasEdgeToEdgeEnabled(source)) {
49
+ log_1.logger.info('Edge-to-edge migration: Activity already enables edge-to-edge, skipping.');
50
+ return;
51
+ }
52
+ const patched = patchActivitySource(source, language);
53
+ if (!patched) {
54
+ log_1.logger.warn(`Edge-to-edge migration: could not patch ${path}. ${manualInstructions(language.enableCall)}`);
55
+ return;
56
+ }
57
+ (0, fs_extra_1.writeFileSync)(path, patched, 'utf-8');
58
+ }
59
+ exports.migrateToEdgeToEdge = migrateToEdgeToEdge;
60
+ function manualInstructions(enableCall) {
61
+ return (`Add ${enableCall} to the onCreate of the Activity hosting Capacitor. ` +
62
+ `Without it the 'native' insetsHandling default of the SystemBars plugin cannot apply safe area insets.`);
63
+ }
64
+ async function findBridgeActivities(config) {
65
+ const javaDir = (0, path_1.join)(config.android.srcMainDirAbs, 'java');
66
+ if (!(0, fs_extra_1.existsSync)(javaDir)) {
67
+ return [];
68
+ }
69
+ const sources = await (0, fs_1.readdirp)(javaDir, {
70
+ filter: (entry) => !entry.stats.isDirectory() && (entry.path.endsWith('.java') || entry.path.endsWith('.kt')),
71
+ });
72
+ return sources.filter((path) => (0, fs_extra_1.readFileSync)(path, 'utf-8').includes('BridgeActivity'));
73
+ }
74
+ function hasEdgeToEdgeEnabled(source) {
75
+ return source.includes('EdgeToEdge.enable(') || source.includes('enableEdgeToEdge(');
76
+ }
77
+ function patchActivitySource(source, language) {
78
+ var _a;
79
+ const patched = (_a = insertEnableCall(source, language)) !== null && _a !== void 0 ? _a : insertOnCreate(source, language);
80
+ if (!patched) {
81
+ return undefined;
82
+ }
83
+ return addImports(patched, language.imports);
84
+ }
85
+ function insertEnableCall(source, language) {
86
+ var _a, _b;
87
+ const lines = source.split('\n');
88
+ const superCall = lines.findIndex((line) => line.includes('super.onCreate('));
89
+ if (superCall === -1) {
90
+ return undefined;
91
+ }
92
+ const indentation = (_b = (_a = lines[superCall].match(/^\s*/)) === null || _a === void 0 ? void 0 : _a[0]) !== null && _b !== void 0 ? _b : '';
93
+ lines.splice(superCall + 1, 0, `${indentation}${language.enableCall}`);
94
+ return lines.join('\n');
95
+ }
96
+ function insertOnCreate(source, language) {
97
+ const classDeclaration = source.search(/\bclass\s+\w+/);
98
+ if (classDeclaration === -1) {
99
+ return undefined;
100
+ }
101
+ const bodyStart = source.indexOf('{', classDeclaration);
102
+ if (bodyStart === -1) {
103
+ return undefined;
104
+ }
105
+ const bodyEnd = findMatchingBrace(source, bodyStart);
106
+ if (bodyEnd === -1) {
107
+ return undefined;
108
+ }
109
+ const body = source.slice(0, bodyEnd).replace(/\s*$/, '\n');
110
+ return body + language.onCreate + source.slice(bodyEnd);
111
+ }
112
+ function findMatchingBrace(source, openIndex) {
113
+ let depth = 0;
114
+ for (let i = openIndex; i < source.length; i++) {
115
+ if (source[i] === '{') {
116
+ depth++;
117
+ }
118
+ else if (source[i] === '}') {
119
+ depth--;
120
+ if (depth === 0) {
121
+ return i;
122
+ }
123
+ }
124
+ }
125
+ return -1;
126
+ }
127
+ function addImports(source, statements) {
128
+ const lines = source.split('\n');
129
+ for (const statement of statements) {
130
+ if (!lines.includes(statement)) {
131
+ lines.splice(importInsertIndex(lines, statement), 0, statement);
132
+ }
133
+ }
134
+ return lines.join('\n');
135
+ }
136
+ function importInsertIndex(lines, statement) {
137
+ const sortsAfter = lines.findIndex((line) => line.startsWith('import ') && line > statement);
138
+ if (sortsAfter !== -1) {
139
+ return sortsAfter;
140
+ }
141
+ let lastImport = -1;
142
+ lines.forEach((line, index) => {
143
+ if (line.startsWith('import ')) {
144
+ lastImport = index;
145
+ }
146
+ });
147
+ if (lastImport !== -1) {
148
+ return lastImport + 1;
149
+ }
150
+ return lines.findIndex((line) => line.startsWith('package ')) + 1;
151
+ }
152
+ exports.__testables = {
153
+ JAVA,
154
+ KOTLIN,
155
+ addImports,
156
+ hasEdgeToEdgeEnabled,
157
+ patchActivitySource,
158
+ };