From f1e7c1d3c14e1f1dd2caf913f190c1d678d30cc2 Mon Sep 17 00:00:00 2001 From: Carl Poole Date: Fri, 8 Apr 2022 10:10:48 -0500 Subject: [PATCH 01/12] more changes --- cli/src/definitions.ts | 3 ++- cli/src/tasks/copy.ts | 52 ++++++++++++++++++++++++++++++++++++++---- 2 files changed, 49 insertions(+), 6 deletions(-) diff --git a/cli/src/definitions.ts b/cli/src/definitions.ts index ba79b71200..445d5312b8 100644 --- a/cli/src/definitions.ts +++ b/cli/src/definitions.ts @@ -1,4 +1,4 @@ -import type { CapacitorConfig } from './declarations'; +import type { CapacitorConfig, PluginsConfig } from './declarations'; type DeepReadonly = { readonly [P in keyof T]: DeepReadonly }; @@ -119,6 +119,7 @@ export interface Config { readonly web: WebConfig; readonly cli: CLIConfig; readonly app: AppConfig; + readonly plugins?: PluginsConfig; } export interface FrameworkConfig { diff --git a/cli/src/tasks/copy.ts b/cli/src/tasks/copy.ts index 8129cb8b40..4b476d5ef7 100644 --- a/cli/src/tasks/copy.ts +++ b/cli/src/tasks/copy.ts @@ -1,5 +1,5 @@ import { copy as fsCopy, pathExists, remove, writeJSON } from '@ionic/utils-fs'; -import { basename, join, relative } from 'path'; +import { basename, join, relative, resolve } from 'path'; import c from '../colors'; import { @@ -18,6 +18,7 @@ import { import type { Config } from '../definitions'; import { isFatal } from '../errors'; import { logger } from '../log'; +import { getPlugins } from '../plugin'; import { allSerial } from '../util/promise'; import { copyWeb } from '../web/copy'; @@ -70,13 +71,30 @@ export async function copy( 'capacitor:copy:before', ); + // Check if @capacitor/federated is present in package.json or if plugins/federation is in the capacitor config? + // if check passes, call copyFederatedDirs + // else run normal copy + const allPlugins = await getPlugins(config, platformName); + var isFederated: boolean = false; + if (allPlugins.filter(plugin => plugin.id === '@capacitor/federation').length > 0) { + isFederated = true; + } + if (platformName === config.ios.name) { - await copyWebDir(config, await config.ios.webDirAbs); + if (isFederated) { + await copyFederatedWebDirs(config, await config.ios.webDirAbs) + } else { + await copyWebDir(config, await config.ios.webDirAbs, config.app.webDirAbs); + } await copyCapacitorConfig(config, config.ios.nativeTargetDirAbs); const cordovaPlugins = await getCordovaPlugins(config, platformName); await handleCordovaPluginsJS(cordovaPlugins, config, platformName); } else if (platformName === config.android.name) { - await copyWebDir(config, config.android.webDirAbs); + if (isFederated) { + await copyFederatedWebDirs(config, await config.android.webDirAbs) + } else { + await copyWebDir(config, config.android.webDirAbs, config.app.webDirAbs); + } await copyCapacitorConfig(config, config.android.assetsDirAbs); const cordovaPlugins = await getCordovaPlugins(config, platformName); await handleCordovaPluginsJS(cordovaPlugins, config, platformName); @@ -110,8 +128,7 @@ async function copyCapacitorConfig(config: Config, nativeAbsDir: string) { ); } -async function copyWebDir(config: Config, nativeAbsDir: string) { - const webAbsDir = config.app.webDirAbs; +async function copyWebDir(config: Config, nativeAbsDir: string, webAbsDir: string) { const webRelDir = basename(webAbsDir); const nativeRelDir = relative(config.app.rootDir, nativeAbsDir); @@ -138,3 +155,28 @@ async function copyWebDir(config: Config, nativeAbsDir: string) { }, ); } + +async function copyFederatedWebDirs(config: Config, nativeAbsDir: string) { + interface FedApp { + name: string, + webDir: string, + appId: string + } + + logger.info("Federated Capacitor Plugin Loaded - Copying Web Assets") + if (config.app.extConfig.plugins && config.app.extConfig.plugins.Federation) { + const fedConfig = config.app.extConfig.plugins.Federation + const fedApps = fedConfig.apps as unknown as FedApp[] + const shellApp = fedConfig.shell as unknown as FedApp + shellApp.webDir = shellApp.webDir ? resolve(config.app.rootDir, shellApp.webDir) : config.app.webDirAbs; + fedApps.push(shellApp) + + await Promise.all(fedApps.map(app => { + const appDir = resolve(config.app.rootDir, app.webDir) + copyWebDir(config, resolve(nativeAbsDir, app.name), appDir) + })) + } else { + // Federated Plugin is present but config not set + } +} + From 38e53f3e4bed00640ba39e458721fffe895979a7 Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Fri, 8 Apr 2022 10:47:17 -0500 Subject: [PATCH 02/12] Updated types for Federation plugin --- cli/src/declarations.ts | 10 ++++++ cli/src/tasks/copy.ts | 72 +++++++++++++++++++++++++++-------------- 2 files changed, 57 insertions(+), 25 deletions(-) diff --git a/cli/src/declarations.ts b/cli/src/declarations.ts index f609d095e3..f9af8f62fa 100644 --- a/cli/src/declarations.ts +++ b/cli/src/declarations.ts @@ -474,12 +474,22 @@ export interface CapacitorConfig { includePlugins?: string[]; } +export interface Federation { + name: string; + webDir: string; + appId: string; +} + export interface PluginsConfig { /** * Plugin configuration by class name. * * @since 1.0.0 */ + Federation?: { + shell: Federation; + apps: Federation[]; + }; [key: string]: | { [key: string]: any; diff --git a/cli/src/tasks/copy.ts b/cli/src/tasks/copy.ts index 4b476d5ef7..402f0ee6ba 100644 --- a/cli/src/tasks/copy.ts +++ b/cli/src/tasks/copy.ts @@ -15,6 +15,7 @@ import { handleCordovaPluginsJS, writeCordovaAndroidManifest, } from '../cordova'; +import { Federation } from '../declarations'; import type { Config } from '../definitions'; import { isFatal } from '../errors'; import { logger } from '../log'; @@ -76,24 +77,35 @@ export async function copy( // else run normal copy const allPlugins = await getPlugins(config, platformName); var isFederated: boolean = false; - if (allPlugins.filter(plugin => plugin.id === '@capacitor/federation').length > 0) { + if ( + allPlugins.filter(plugin => plugin.id === '@capacitor/federation') + .length > 0 + ) { isFederated = true; } if (platformName === config.ios.name) { if (isFederated) { - await copyFederatedWebDirs(config, await config.ios.webDirAbs) + await copyFederatedWebDirs(config, await config.ios.webDirAbs); } else { - await copyWebDir(config, await config.ios.webDirAbs, config.app.webDirAbs); + await copyWebDir( + config, + await config.ios.webDirAbs, + config.app.webDirAbs, + ); } await copyCapacitorConfig(config, config.ios.nativeTargetDirAbs); const cordovaPlugins = await getCordovaPlugins(config, platformName); await handleCordovaPluginsJS(cordovaPlugins, config, platformName); } else if (platformName === config.android.name) { if (isFederated) { - await copyFederatedWebDirs(config, await config.android.webDirAbs) + await copyFederatedWebDirs(config, await config.android.webDirAbs); } else { - await copyWebDir(config, config.android.webDirAbs, config.app.webDirAbs); + await copyWebDir( + config, + config.android.webDirAbs, + config.app.webDirAbs, + ); } await copyCapacitorConfig(config, config.android.assetsDirAbs); const cordovaPlugins = await getCordovaPlugins(config, platformName); @@ -105,6 +117,7 @@ export async function copy( throw `Platform ${platformName} is not valid.`; } }); + await runPlatformHook( config, platformName, @@ -128,7 +141,11 @@ async function copyCapacitorConfig(config: Config, nativeAbsDir: string) { ); } -async function copyWebDir(config: Config, nativeAbsDir: string, webAbsDir: string) { +async function copyWebDir( + config: Config, + nativeAbsDir: string, + webAbsDir: string, +) { const webRelDir = basename(webAbsDir); const nativeRelDir = relative(config.app.rootDir, nativeAbsDir); @@ -157,26 +174,31 @@ async function copyWebDir(config: Config, nativeAbsDir: string, webAbsDir: strin } async function copyFederatedWebDirs(config: Config, nativeAbsDir: string) { - interface FedApp { - name: string, - webDir: string, - appId: string - } + logger.info('Federated Capacitor Plugin Loaded - Copying Web Assets'); - logger.info("Federated Capacitor Plugin Loaded - Copying Web Assets") - if (config.app.extConfig.plugins && config.app.extConfig.plugins.Federation) { - const fedConfig = config.app.extConfig.plugins.Federation - const fedApps = fedConfig.apps as unknown as FedApp[] - const shellApp = fedConfig.shell as unknown as FedApp - shellApp.webDir = shellApp.webDir ? resolve(config.app.rootDir, shellApp.webDir) : config.app.webDirAbs; - fedApps.push(shellApp) - - await Promise.all(fedApps.map(app => { - const appDir = resolve(config.app.rootDir, app.webDir) - copyWebDir(config, resolve(nativeAbsDir, app.name), appDir) - })) - } else { - // Federated Plugin is present but config not set + if (!config.app.extConfig?.plugins?.Federation) { + return; + } + const fedConfig = config.app.extConfig.plugins.Federation; + if (!isFederation(fedConfig.shell)) { + return; } + if (!fedConfig.apps.every(isFederation)) { + return; + } + + await Promise.all( + [...fedConfig.apps, fedConfig.shell].map(app => { + const appDir = resolve(config.app.rootDir, app.webDir); + copyWebDir(config, resolve(nativeAbsDir, app.name), appDir); + }), + ); } +function isFederation(config: any): config is Federation { + return ( + (config as Federation).webDir !== undefined && + (config as Federation).name !== undefined && + (config as Federation).appId !== undefined + ); +} From 69dadde0d1d4b8e1c1f6c6d833beab3fc2a9265e Mon Sep 17 00:00:00 2001 From: Carl Poole Date: Fri, 8 Apr 2022 14:45:19 -0500 Subject: [PATCH 03/12] moved Fed cap definition and throw errors --- cli/src/declarations.ts | 14 ++++++++++---- cli/src/tasks/copy.ts | 10 ++++++---- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/cli/src/declarations.ts b/cli/src/declarations.ts index f9af8f62fa..ed610e2bb8 100644 --- a/cli/src/declarations.ts +++ b/cli/src/declarations.ts @@ -486,13 +486,19 @@ export interface PluginsConfig { * * @since 1.0.0 */ - Federation?: { - shell: Federation; - apps: Federation[]; - }; [key: string]: | { [key: string]: any; } | undefined; + + /** + * Capacitor Federation plugin configuration + * + * @since 3.5.0 + */ + Federation?: { + shell: Federation; + apps: Federation[]; + }; } diff --git a/cli/src/tasks/copy.ts b/cli/src/tasks/copy.ts index 402f0ee6ba..baa46d8a98 100644 --- a/cli/src/tasks/copy.ts +++ b/cli/src/tasks/copy.ts @@ -177,20 +177,22 @@ async function copyFederatedWebDirs(config: Config, nativeAbsDir: string) { logger.info('Federated Capacitor Plugin Loaded - Copying Web Assets'); if (!config.app.extConfig?.plugins?.Federation) { - return; + throw `Federated Capacitor plugin is present but no valid config is defined.`; } + const fedConfig = config.app.extConfig.plugins.Federation; if (!isFederation(fedConfig.shell)) { - return; + throw `Federated Capacitor plugin is present but no valid Shell application is defined in the config.`; } + if (!fedConfig.apps.every(isFederation)) { - return; + throw `Federated Capacitor plugin is present but there is a problem with the apps defined in the config.`; } await Promise.all( [...fedConfig.apps, fedConfig.shell].map(app => { const appDir = resolve(config.app.rootDir, app.webDir); - copyWebDir(config, resolve(nativeAbsDir, app.name), appDir); + return copyWebDir(config, resolve(nativeAbsDir, app.name), appDir); }), ); } From 7a40c8af7a85ae63c376a12f30168b6dc0fb5ba7 Mon Sep 17 00:00:00 2001 From: Carl Poole Date: Fri, 8 Apr 2022 14:49:50 -0500 Subject: [PATCH 04/12] Removed old comment and lint checks --- cli/src/tasks/copy.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/cli/src/tasks/copy.ts b/cli/src/tasks/copy.ts index baa46d8a98..8bd618e738 100644 --- a/cli/src/tasks/copy.ts +++ b/cli/src/tasks/copy.ts @@ -15,7 +15,7 @@ import { handleCordovaPluginsJS, writeCordovaAndroidManifest, } from '../cordova'; -import { Federation } from '../declarations'; +import type { Federation } from '../declarations'; import type { Config } from '../definitions'; import { isFatal } from '../errors'; import { logger } from '../log'; @@ -72,11 +72,8 @@ export async function copy( 'capacitor:copy:before', ); - // Check if @capacitor/federated is present in package.json or if plugins/federation is in the capacitor config? - // if check passes, call copyFederatedDirs - // else run normal copy const allPlugins = await getPlugins(config, platformName); - var isFederated: boolean = false; + let isFederated = false; if ( allPlugins.filter(plugin => plugin.id === '@capacitor/federation') .length > 0 From ce5f9b9fe2ac08e0ba6da2dd2540e8e8fe84caf0 Mon Sep 17 00:00:00 2001 From: Carl Poole Date: Fri, 8 Apr 2022 15:14:23 -0500 Subject: [PATCH 05/12] Removed unnecessary await from android webDirAbs --- cli/src/tasks/copy.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/src/tasks/copy.ts b/cli/src/tasks/copy.ts index 8bd618e738..80b38e2e80 100644 --- a/cli/src/tasks/copy.ts +++ b/cli/src/tasks/copy.ts @@ -96,7 +96,7 @@ export async function copy( await handleCordovaPluginsJS(cordovaPlugins, config, platformName); } else if (platformName === config.android.name) { if (isFederated) { - await copyFederatedWebDirs(config, await config.android.webDirAbs); + await copyFederatedWebDirs(config, config.android.webDirAbs); } else { await copyWebDir( config, From fb6f7bc13897b6a5fed4a28308ab6e1146e469f5 Mon Sep 17 00:00:00 2001 From: Carl Poole Date: Wed, 13 Apr 2022 11:12:04 -0500 Subject: [PATCH 06/12] skip bundledWebRuntime for fedcap --- cli/src/tasks/copy.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/cli/src/tasks/copy.ts b/cli/src/tasks/copy.ts index 80b38e2e80..b008c32ef0 100644 --- a/cli/src/tasks/copy.ts +++ b/cli/src/tasks/copy.ts @@ -109,7 +109,11 @@ export async function copy( await handleCordovaPluginsJS(cordovaPlugins, config, platformName); await writeCordovaAndroidManifest(cordovaPlugins, config, platformName); } else if (platformName === config.web.name) { - await copyWeb(config); + if (isFederated) { + logger.info('Federated Capacitor Plugin installed, skipping web bundling...'); + } else { + await copyWeb(config); + } } else { throw `Platform ${platformName} is not valid.`; } From 4943af6f34ac46b3f837bcf09d7372f1f822f4c7 Mon Sep 17 00:00:00 2001 From: Carl Poole Date: Mon, 18 Apr 2022 10:47:38 -0500 Subject: [PATCH 07/12] appId optional --- cli/src/tasks/copy.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cli/src/tasks/copy.ts b/cli/src/tasks/copy.ts index b008c32ef0..beee708cea 100644 --- a/cli/src/tasks/copy.ts +++ b/cli/src/tasks/copy.ts @@ -201,7 +201,6 @@ async function copyFederatedWebDirs(config: Config, nativeAbsDir: string) { function isFederation(config: any): config is Federation { return ( (config as Federation).webDir !== undefined && - (config as Federation).name !== undefined && - (config as Federation).appId !== undefined + (config as Federation).name !== undefined ); } From ac68ea8dd3b9d2630d034f97e48449cbad09ba2a Mon Sep 17 00:00:00 2001 From: Carl Poole Date: Mon, 18 Apr 2022 10:58:54 -0500 Subject: [PATCH 08/12] fmt --- cli/src/tasks/copy.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cli/src/tasks/copy.ts b/cli/src/tasks/copy.ts index beee708cea..47e9a9431d 100644 --- a/cli/src/tasks/copy.ts +++ b/cli/src/tasks/copy.ts @@ -110,7 +110,9 @@ export async function copy( await writeCordovaAndroidManifest(cordovaPlugins, config, platformName); } else if (platformName === config.web.name) { if (isFederated) { - logger.info('Federated Capacitor Plugin installed, skipping web bundling...'); + logger.info( + 'Federated Capacitor Plugin installed, skipping web bundling...', + ); } else { await copyWeb(config); } From e3029e16f3b6563ac4f111470f4b75141a4bf64e Mon Sep 17 00:00:00 2001 From: Carl Poole Date: Mon, 18 Apr 2022 14:53:12 -0500 Subject: [PATCH 09/12] naming to Capacitor Portals --- cli/src/tasks/copy.ts | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/cli/src/tasks/copy.ts b/cli/src/tasks/copy.ts index 47e9a9431d..0396cc5fb0 100644 --- a/cli/src/tasks/copy.ts +++ b/cli/src/tasks/copy.ts @@ -15,7 +15,7 @@ import { handleCordovaPluginsJS, writeCordovaAndroidManifest, } from '../cordova'; -import type { Federation } from '../declarations'; +import type { Portal } from '../declarations'; import type { Config } from '../definitions'; import { isFatal } from '../errors'; import { logger } from '../log'; @@ -73,16 +73,16 @@ export async function copy( ); const allPlugins = await getPlugins(config, platformName); - let isFederated = false; + let usesCapacitorPortals = false; if ( allPlugins.filter(plugin => plugin.id === '@capacitor/federation') .length > 0 ) { - isFederated = true; + usesCapacitorPortals = true; } if (platformName === config.ios.name) { - if (isFederated) { + if (usesCapacitorPortals) { await copyFederatedWebDirs(config, await config.ios.webDirAbs); } else { await copyWebDir( @@ -95,7 +95,7 @@ export async function copy( const cordovaPlugins = await getCordovaPlugins(config, platformName); await handleCordovaPluginsJS(cordovaPlugins, config, platformName); } else if (platformName === config.android.name) { - if (isFederated) { + if (usesCapacitorPortals) { await copyFederatedWebDirs(config, config.android.webDirAbs); } else { await copyWebDir( @@ -109,9 +109,9 @@ export async function copy( await handleCordovaPluginsJS(cordovaPlugins, config, platformName); await writeCordovaAndroidManifest(cordovaPlugins, config, platformName); } else if (platformName === config.web.name) { - if (isFederated) { + if (usesCapacitorPortals) { logger.info( - 'Federated Capacitor Plugin installed, skipping web bundling...', + 'Capacitor Portals Plugin installed, skipping web bundling...', ); } else { await copyWeb(config); @@ -177,32 +177,32 @@ async function copyWebDir( } async function copyFederatedWebDirs(config: Config, nativeAbsDir: string) { - logger.info('Federated Capacitor Plugin Loaded - Copying Web Assets'); + logger.info('Capacitor Portals Plugin Loaded - Copying Web Assets'); - if (!config.app.extConfig?.plugins?.Federation) { - throw `Federated Capacitor plugin is present but no valid config is defined.`; + if (!config.app.extConfig?.plugins?.Portals) { + throw `Capacitor Portals plugin is present but no valid config is defined.`; } - const fedConfig = config.app.extConfig.plugins.Federation; - if (!isFederation(fedConfig.shell)) { - throw `Federated Capacitor plugin is present but no valid Shell application is defined in the config.`; + const portalsConfig = config.app.extConfig.plugins.Portals; + if (!isPortal(portalsConfig.shell)) { + throw `Capacitor Portals plugin is present but no valid Shell application is defined in the config.`; } - if (!fedConfig.apps.every(isFederation)) { - throw `Federated Capacitor plugin is present but there is a problem with the apps defined in the config.`; + if (!portalsConfig.apps.every(isPortal)) { + throw `Capacitor Portals plugin is present but there is a problem with the apps defined in the config.`; } await Promise.all( - [...fedConfig.apps, fedConfig.shell].map(app => { + [...portalsConfig.apps, portalsConfig.shell].map(app => { const appDir = resolve(config.app.rootDir, app.webDir); return copyWebDir(config, resolve(nativeAbsDir, app.name), appDir); }), ); } -function isFederation(config: any): config is Federation { +function isPortal(config: any): config is Portal { return ( - (config as Federation).webDir !== undefined && - (config as Federation).name !== undefined + (config as Portal).webDir !== undefined && + (config as Portal).name !== undefined ); } From 817c1addd81e5760fbb6a569bd49cf9a91e69b35 Mon Sep 17 00:00:00 2001 From: Carl Poole Date: Mon, 18 Apr 2022 14:54:29 -0500 Subject: [PATCH 10/12] Portal object --- cli/src/declarations.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/cli/src/declarations.ts b/cli/src/declarations.ts index ed610e2bb8..d03229cae6 100644 --- a/cli/src/declarations.ts +++ b/cli/src/declarations.ts @@ -474,10 +474,10 @@ export interface CapacitorConfig { includePlugins?: string[]; } -export interface Federation { +export interface Portal { name: string; webDir: string; - appId: string; + appId?: string; } export interface PluginsConfig { @@ -497,8 +497,8 @@ export interface PluginsConfig { * * @since 3.5.0 */ - Federation?: { - shell: Federation; - apps: Federation[]; + Portals?: { + shell: Portal; + apps: Portal[]; }; } From 5285f0d0426103d54b2eff6da6da59470057a9c9 Mon Sep 17 00:00:00 2001 From: Carl Poole Date: Thu, 21 Apr 2022 13:45:01 -0500 Subject: [PATCH 11/12] plugin info update --- cli/src/declarations.ts | 2 +- cli/src/tasks/copy.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cli/src/declarations.ts b/cli/src/declarations.ts index d03229cae6..dc17d5befb 100644 --- a/cli/src/declarations.ts +++ b/cli/src/declarations.ts @@ -493,7 +493,7 @@ export interface PluginsConfig { | undefined; /** - * Capacitor Federation plugin configuration + * Capacitor Portals plugin configuration * * @since 3.5.0 */ diff --git a/cli/src/tasks/copy.ts b/cli/src/tasks/copy.ts index 0396cc5fb0..87a32cd38a 100644 --- a/cli/src/tasks/copy.ts +++ b/cli/src/tasks/copy.ts @@ -75,7 +75,7 @@ export async function copy( const allPlugins = await getPlugins(config, platformName); let usesCapacitorPortals = false; if ( - allPlugins.filter(plugin => plugin.id === '@capacitor/federation') + allPlugins.filter(plugin => plugin.id === '@ionic-enterprise/capacitor-portals') .length > 0 ) { usesCapacitorPortals = true; From 8b7eb58f09b99ea532237c8224eea5dbdd876e1f Mon Sep 17 00:00:00 2001 From: Carl Poole Date: Thu, 21 Apr 2022 13:50:04 -0500 Subject: [PATCH 12/12] fmt --- cli/src/tasks/copy.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cli/src/tasks/copy.ts b/cli/src/tasks/copy.ts index 87a32cd38a..2cde358c8b 100644 --- a/cli/src/tasks/copy.ts +++ b/cli/src/tasks/copy.ts @@ -75,8 +75,9 @@ export async function copy( const allPlugins = await getPlugins(config, platformName); let usesCapacitorPortals = false; if ( - allPlugins.filter(plugin => plugin.id === '@ionic-enterprise/capacitor-portals') - .length > 0 + allPlugins.filter( + plugin => plugin.id === '@ionic-enterprise/capacitor-portals', + ).length > 0 ) { usesCapacitorPortals = true; }