From e073b10721111d89c04e9904da322c16642a4ad5 Mon Sep 17 00:00:00 2001 From: Daniel Imhoff Date: Tue, 17 Nov 2020 12:17:42 -0800 Subject: [PATCH 01/34] feat(capacitor): Capacitor 3 support --- .../@ionic/cli/src/commands/capacitor/base.ts | 16 +++ .../@ionic/cli/src/commands/capacitor/run.ts | 114 +++++++++++++----- packages/@ionic/cli/src/definitions.ts | 7 ++ .../@ionic/cli/src/lib/integrations/index.ts | 15 +-- packages/@ionic/cli/src/lib/project/index.ts | 9 +- 5 files changed, 121 insertions(+), 40 deletions(-) diff --git a/packages/@ionic/cli/src/commands/capacitor/base.ts b/packages/@ionic/cli/src/commands/capacitor/base.ts index 67cd26a82e..119b45537f 100644 --- a/packages/@ionic/cli/src/commands/capacitor/base.ts +++ b/packages/@ionic/cli/src/commands/capacitor/base.ts @@ -1,6 +1,7 @@ import { pathExists } from '@ionic/utils-fs'; import { ERROR_COMMAND_NOT_FOUND, ERROR_SIGNAL_EXIT, SubprocessError } from '@ionic/utils-subprocess'; import * as path from 'path'; +import * as semver from 'semver'; import { CommandInstanceInfo, CommandLineInputs, CommandLineOptions, IonicCapacitorOptions, ProjectIntegration } from '../../definitions'; import { input, strong } from '../../lib/color'; @@ -33,6 +34,21 @@ export abstract class CapacitorCommand extends Command { return new CapacitorConfig(path.resolve(this.project.directory, CAPACITOR_CONFIG_FILE)); } + async getCapacitorVersion(): Promise { + if (!this.project) { + throw new FatalException(`Cannot use Capacitor outside a project directory..`); + } + + const capacitor = await this.project.createIntegration('capacitor'); + const version = semver.parse(await capacitor.getCapacitorCLIVersion()); + + if (!version) { + throw new FatalException('Error while getting Capacitor CLI version. Is Capacitor installed?'); + } + + return version; + } + async checkCapacitor(runinfo: CommandInstanceInfo) { if (!this.project) { throw new FatalException(`Cannot use Capacitor outside a project directory.`); diff --git a/packages/@ionic/cli/src/commands/capacitor/run.ts b/packages/@ionic/cli/src/commands/capacitor/run.ts index bab4bbf91b..69b7b8b74c 100644 --- a/packages/@ionic/cli/src/commands/capacitor/run.ts +++ b/packages/@ionic/cli/src/commands/capacitor/run.ts @@ -4,7 +4,7 @@ import * as chalk from 'chalk'; import * as lodash from 'lodash'; import { AnyBuildOptions, AnyServeOptions, CapacitorRunHookName, CommandInstanceInfo, CommandLineInputs, CommandLineOptions, CommandMetadata, CommandMetadataOption, CommandPreRun } from '../../definitions'; -import { input, strong, weak } from '../../lib/color'; +import { ancillary, input, strong, weak } from '../../lib/color'; import { FatalException, RunnerException } from '../../lib/errors'; import { Hook, HookDeps } from '../../lib/hooks'; import { generateOptionsForCapacitorBuild, getNativeIDEForPlatform, getVirtualDeviceNameForPlatform } from '../../lib/integrations/capacitor/utils'; @@ -24,19 +24,33 @@ export class RunCommand extends CapacitorCommand implements CommandPreRun { ].sort(); let options: CommandMetadataOption[] = [ - // Build Options { - name: 'build', - summary: 'Do not invoke Ionic build', + name: 'list', + summary: 'List all available targets', type: Boolean, - default: true, + groups: ['capacitor', 'native-run'], + hint: weak('[capacitor]'), + }, + { + name: 'target', + summary: `Deploy to a specific device by its ID (use ${input('--list')} to see all)`, + type: String, + groups: ['capacitor', 'native-run'], + hint: weak('[capacitor]'), }, { name: 'open', - summary: 'Do not invoke Capacitor open', + summary: `Open native IDE instead of using ${input('capacitor run')}`, + type: Boolean, + }, + // Build Options + { + name: 'build', + summary: 'Do not invoke Ionic build', type: Boolean, default: true, }, + // Serve Options ...COMMON_SERVE_COMMAND_OPTIONS.filter(o => !['livereload'].includes(o.name)).map(o => ({ ...o, hint: weak('(--livereload)') })), { name: 'livereload', @@ -57,6 +71,11 @@ export class RunCommand extends CapacitorCommand implements CommandPreRun { url: 'https://ionicframework.com/docs/developer-resources/developer-tips', shortUrl: 'https://ion.link/remote-debugging-docs', }, + { + id: 'livereload-docs', + url: 'https://ionicframework.com/docs/cli/livereload', + shortUrl: 'https://ion.link/livereload-docs', + }, ]; const serveRunner = this.project && await this.project.getServeRunner(); @@ -87,12 +106,11 @@ export class RunCommand extends CapacitorCommand implements CommandPreRun { description: ` ${input('ionic capacitor run')} will do the following: - Perform ${input('ionic build')} (or run the dev server from ${input('ionic serve')} with the ${input('--livereload')} option) -- Copy web assets into the specified native platform -- Open the IDE for your native project (Xcode for iOS, Android Studio for Android) +- Run ${input('capacitor run')} (or open IDE for your native project with the ${input('--open')} option) -When using the ${input('--livereload')} option and need to serve to your LAN, a device, or an emulator, use the ${input('--external')} option also. Otherwise, the web view tries to access ${input('localhost')}. +When using ${input('--livereload')} with hardware devices, remember that livereload needs an active connection between device and computer. In some scenarios, you may need to host the dev server on an external address using the ${input('--external')} option. See these docs[^livereload-docs] for more information. -Once the web assets and configuration are copied into your native project, the app can run on devices and emulators/simulators using the native IDE. Unfortunately, programmatically building and launching the native project is not yet supported. +If you have multiple devices and emulators, you can target a specific one by ID with the ${input('--target')} option. You can list targets with ${input('--list')}. For Android and iOS, you can setup Remote Debugging on your device with browser development tools using these docs[^remote-debugging-docs]. `, @@ -138,11 +156,27 @@ For Android and iOS, you can setup Remote Debugging on your device with browser async run(inputs: CommandLineInputs, options: CommandLineOptions): Promise { if (!this.project) { - throw new FatalException(`Cannot run ${input('ionic capacitor run/emulate')} outside a project directory.`); + throw new FatalException(`Cannot run ${input('ionic capacitor run')} outside a project directory.`); } const [ platform ] = inputs; + if (options['list']) { + await this.runCapacitor(['run', platform, '--list']); + throw new FatalException('', 0); + } + + const version = await this.getCapacitorVersion(); + const isOldCapacitor = version.major < 3; + + if (isOldCapacitor) { + this.env.log.warn( + `Support for Capacitor 1 and 2 is deprecated.\n` + + `Please update to the latest Capacitor. Visit the docs${ancillary('[1]')} for upgrade guides.\n\n` + + `${ancillary('[1]')}: ${strong('https://capacitorjs.com/docs/')}\n` + ); + } + try { if (options['livereload']) { await this.runServe(inputs, options); @@ -157,25 +191,10 @@ For Android and iOS, you can setup Remote Debugging on your device with browser throw e; } - // copy assets and capacitor.config.json into place - await this.runCapacitor(['copy', platform]); - - // TODO: native-run - - const hookDeps: HookDeps = { - config: this.env.config, - project: this.project, - shell: this.env.shell, - }; - - await this.runCapacitorRunHook('capacitor:run:before', inputs, options, hookDeps); - - if (options['open']) { - this.env.log.nl(); - this.env.log.info(this.getContinueMessage(platform)); - this.env.log.nl(); - - await this.runCapacitor(['open', platform]); + if (isOldCapacitor || options['open'] === true) { + await this.runCapacitorOpenFlow(inputs, options); + } else { + await this.runCapacitorRunFlow(inputs, options); } if (options['livereload']) { @@ -184,13 +203,14 @@ For Android and iOS, you can setup Remote Debugging on your device with browser 'Development server will continue running until manually stopped.\n' + chalk.yellow('Use Ctrl+C to quit this process') ); + await sleepForever(); } } async runServe(inputs: CommandLineInputs, options: CommandLineOptions): Promise { if (!this.project) { - throw new FatalException(`Cannot run ${input('ionic capacitor run/emulate')} outside a project directory.`); + throw new FatalException(`Cannot run ${input('ionic capacitor run')} outside a project directory.`); } const [ platform ] = inputs; @@ -221,6 +241,25 @@ For Android and iOS, you can setup Remote Debugging on your device with browser conf.setServerUrl(serverUrl); } + protected async runCapacitorOpenFlow(inputs: CommandLineInputs, options: CommandLineOptions): Promise { + if (!this.project) { + throw new FatalException(`Cannot run ${input('ionic capacitor run')} outside a project directory.`); + } + + const [ platform ] = inputs; + + await this.runCapacitor(['copy', platform]); + await this.runCapacitorRunHook('capacitor:run:before', inputs, options, { ...this.env, project: this.project }); + + if (options['open'] !== false) { + this.env.log.nl(); + this.env.log.info(this.getContinueMessage(platform)); + this.env.log.nl(); + + await this.runCapacitor(['open', platform]); + } + } + protected getContinueMessage(platform: string): string { if (platform === 'electron') { return 'Ready to be used in Electron!'; @@ -232,7 +271,18 @@ For Android and iOS, you can setup Remote Debugging on your device with browser ); } - private async runCapacitorRunHook(name: CapacitorRunHookName, inputs: CommandLineInputs, options: CommandLineOptions, e: HookDeps): Promise { + protected async runCapacitorRunFlow(inputs: CommandLineInputs, options: CommandLineOptions): Promise { + if (!this.project) { + throw new FatalException(`Cannot run ${input('ionic capacitor run')} outside a project directory.`); + } + + const [ platform ] = inputs; + + await this.runCapacitorRunHook('capacitor:run:before', inputs, options, { ...this.env, project: this.project }); + await this.runCapacitor(['run', platform, ...(typeof options['target'] === 'string' ? ['--target', options['target']] : [])]); + } + + protected async runCapacitorRunHook(name: CapacitorRunHookName, inputs: CommandLineInputs, options: CommandLineOptions, e: HookDeps): Promise { const hook = new CapacitorRunHook(name, e); let serveOptions: AnyServeOptions | undefined; let buildOptions: AnyBuildOptions | undefined; diff --git a/packages/@ionic/cli/src/definitions.ts b/packages/@ionic/cli/src/definitions.ts index 69ed628554..249ceaee2d 100644 --- a/packages/@ionic/cli/src/definitions.ts +++ b/packages/@ionic/cli/src/definitions.ts @@ -19,6 +19,10 @@ import { Subprocess, SubprocessOptions, WhichOptions } from '@ionic/utils-subpro import { ChildProcess, SpawnOptions } from 'child_process'; import * as fs from 'fs'; +import type { Integration as CapacitorIntegration } from './lib/integrations/capacitor'; +import type { Integration as CordovaIntegration } from './lib/integrations/cordova'; +import type { Integration as EnterpriseIntegration } from './lib/integrations/enterprise'; + export { CommandLineInputs, CommandLineOptions, @@ -348,6 +352,9 @@ export interface IProject { getDistDir(): Promise; getInfo(): Promise; detected(): Promise; + createIntegration(name: 'capacitor'): Promise; + createIntegration(name: 'cordova'): Promise; + createIntegration(name: 'enterprise'): Promise; createIntegration(name: IntegrationName): Promise>; getIntegration(name: IntegrationName): Required | undefined; requireIntegration(name: IntegrationName): Required; diff --git a/packages/@ionic/cli/src/lib/integrations/index.ts b/packages/@ionic/cli/src/lib/integrations/index.ts index 8df921dd74..58176fa002 100644 --- a/packages/@ionic/cli/src/lib/integrations/index.ts +++ b/packages/@ionic/cli/src/lib/integrations/index.ts @@ -19,6 +19,9 @@ import { import { isIntegrationName } from '../../guards'; import { strong } from '../color'; import { IntegrationNotFoundException } from '../errors'; +import type { Integration as CapacitorIntegration } from './capacitor'; +import type { Integration as CordovaIntegration } from './cordova'; +import type { Integration as EnterpriseIntegration } from './enterprise'; export { INTEGRATION_NAMES } from '../../guards'; @@ -36,8 +39,6 @@ export interface IntegrationDeps { readonly log: ILogger; } -export type IntegationUnion = import('./capacitor').Integration | import('./cordova').Integration | import('./enterprise').Integration; - export class IntegrationConfig extends BaseConfig { provideDefaults(c: Partial>): ProjectIntegration { return {}; @@ -52,11 +53,11 @@ export abstract class BaseIntegration implements I constructor(protected readonly e: IntegrationDeps) {} - static async createFromName(deps: IntegrationDeps, name: 'capacitor'): Promise; - static async createFromName(deps: IntegrationDeps, name: 'cordova'): Promise; - static async createFromName(deps: IntegrationDeps, name: 'enterprise'): Promise; - static async createFromName(deps: IntegrationDeps, name: IntegrationName): Promise>; - static async createFromName(deps: IntegrationDeps, name: IntegrationName): Promise { + static async createFromName(deps: IntegrationDeps, name: 'capacitor'): Promise; + static async createFromName(deps: IntegrationDeps, name: 'cordova'): Promise; + static async createFromName(deps: IntegrationDeps, name: 'enterprise'): Promise; + static async createFromName(deps: IntegrationDeps, name: IntegrationName): Promise; + static async createFromName(deps: IntegrationDeps, name: IntegrationName): Promise { if (isIntegrationName(name)) { const { Integration } = await import(`./${name}`); return new Integration(deps); diff --git a/packages/@ionic/cli/src/lib/project/index.ts b/packages/@ionic/cli/src/lib/project/index.ts index 522c1b3b8b..86515add7a 100644 --- a/packages/@ionic/cli/src/lib/project/index.ts +++ b/packages/@ionic/cli/src/lib/project/index.ts @@ -16,6 +16,9 @@ import { BaseException, FatalException, IntegrationNotFoundException, RunnerNotF import { BaseIntegration } from '../integrations'; import { CAPACITOR_CONFIG_FILE, CapacitorConfig } from '../integrations/capacitor/config'; import { Color } from '../utils/color'; +import type { Integration as CapacitorIntegration } from '../integrations/capacitor'; +import type { Integration as CordovaIntegration } from '../integrations/cordova'; +import type { Integration as EnterpriseIntegration } from '../integrations/enterprise'; const debug = Debug('ionic:lib:project'); @@ -717,7 +720,11 @@ export abstract class Project implements IProject { registry.register(new ailments.CordovaPlatformsCommitted(deps)); } - async createIntegration(name: IntegrationName): Promise> { + async createIntegration(name: 'capacitor'): Promise; + async createIntegration(name: 'cordova'): Promise; + async createIntegration(name: 'enterprise'): Promise; + async createIntegration(name: IntegrationName): Promise; + async createIntegration(name: IntegrationName): Promise { return BaseIntegration.createFromName({ client: this.e.client, config: this.e.config, From d80aed96b5308612ae72b217948ea0e9454d1b59 Mon Sep 17 00:00:00 2001 From: Daniel Imhoff Date: Tue, 17 Nov 2020 15:40:40 -0800 Subject: [PATCH 02/34] dynamic config wip --- .../@ionic/cli/src/commands/capacitor/base.ts | 94 +++++++++++++++---- .../cli/src/commands/capacitor/build.ts | 2 +- .../@ionic/cli/src/commands/capacitor/run.ts | 10 +- .../@ionic/cli/src/commands/capacitor/sync.ts | 2 +- packages/@ionic/cli/src/definitions.ts | 10 +- .../src/lib/integrations/capacitor/config.ts | 4 + .../src/lib/integrations/capacitor/index.ts | 24 ++++- 7 files changed, 114 insertions(+), 32 deletions(-) diff --git a/packages/@ionic/cli/src/commands/capacitor/base.ts b/packages/@ionic/cli/src/commands/capacitor/base.ts index 119b45537f..f562b4bd8a 100644 --- a/packages/@ionic/cli/src/commands/capacitor/base.ts +++ b/packages/@ionic/cli/src/commands/capacitor/base.ts @@ -8,11 +8,14 @@ import { input, strong } from '../../lib/color'; import { Command } from '../../lib/command'; import { FatalException, RunnerException } from '../../lib/errors'; import { runCommand } from '../../lib/executor'; -import { CAPACITOR_CONFIG_FILE, CapacitorConfig } from '../../lib/integrations/capacitor/config'; +import type { CapacitorCLIConfig, Integration as CapacitorIntegration } from '../../lib/integrations/capacitor' +import { CAPACITOR_CONFIG_FILE, CapacitorConfig, CapacitorConfigFile } from '../../lib/integrations/capacitor/config'; import { generateOptionsForCapacitorBuild } from '../../lib/integrations/capacitor/utils'; export abstract class CapacitorCommand extends Command { private _integration?: Required; + private _integrationObject?: CapacitorIntegration; + private _cliconfig?: CapacitorCLIConfig; get integration(): Required { if (!this.project) { @@ -26,20 +29,83 @@ export abstract class CapacitorCommand extends Command { return this._integration; } - getCapacitorConfig(): CapacitorConfig { + async getGeneratedConfig(platform: string): Promise { if (!this.project) { throw new FatalException(`Cannot use Capacitor outside a project directory.`); } - return new CapacitorConfig(path.resolve(this.project.directory, CAPACITOR_CONFIG_FILE)); + const p = await this.getGeneratedConfigPath(platform); + + return new CapacitorConfig(p); } - async getCapacitorVersion(): Promise { + async getGeneratedConfigPath(platform: string): Promise { + if (!this.project) { + throw new FatalException(`Cannot use Capacitor outside a project directory.`); + } + + const p = await this.getGeneratedConfigDir(platform); + + return path.resolve(this.project.directory, p, CAPACITOR_CONFIG_FILE); + } + + async getGeneratedConfigDir(platform: string): Promise { + const cli = await this.getCapacitorCLIConfig(); + + switch (platform) { + case 'android': + return cli?.android.assetsDirAbs ?? 'android/app/src/main/assets'; + case 'ios': + return cli?.ios.nativeTargetDirAbs ?? 'ios/App/App'; + } + + throw new FatalException(`Could not determine generated Capacitor config path for ${input(platform)} platform.`); + } + + async getCapacitorConfig(): Promise { + // try using `capacitor config --json` + const cli = await this.getCapacitorCLIConfig(); + + if (cli) { + return cli.app.extConfig; + } + if (!this.project) { - throw new FatalException(`Cannot use Capacitor outside a project directory..`); + return; } - const capacitor = await this.project.createIntegration('capacitor'); + try { + // fallback to reading capacitor.config.json if it exists + const conf = new CapacitorConfig(path.resolve(this.project.directory, CAPACITOR_CONFIG_FILE)); + return conf.c; + } catch (e) { + // ignore + } + } + + async getCapacitorCLIConfig(): Promise { + if (!this._cliconfig) { + const capacitor = await this.getCapacitorIntegration(); + this._cliconfig = await capacitor.getCapacitorCLIConfig(); + } + + return this._cliconfig; + } + + async getCapacitorIntegration() { + if (!this.project) { + throw new FatalException(`Cannot use Capacitor outside a project directory.`); + } + + if (!this._integrationObject) { + this._integrationObject = await this.project.createIntegration('capacitor'); + } + + return this._integrationObject; + } + + async getCapacitorVersion(): Promise { + const capacitor = await this.getCapacitorIntegration(); const version = semver.parse(await capacitor.getCapacitorCLIVersion()); if (!version) { @@ -100,10 +166,9 @@ export abstract class CapacitorCommand extends Command { throw new FatalException(`Cannot use Capacitor outside a project directory.`); } - const conf = this.getCapacitorConfig(); - const serverConfig = conf.get('server'); + const cli = await this.getCapacitorCLIConfig(); - if (serverConfig && serverConfig.url) { + if (cli?.app.extConfig.server?.url) { this.env.log.warn( `Capacitor server URL is in use.\n` + `This may result in unexpected behavior for this build, where an external server is used in the Web View instead of your app. This likely occurred because of ${input('--livereload')} usage in the past and the CLI improperly exiting without cleaning up.\n\n` + @@ -151,20 +216,15 @@ export abstract class CapacitorCommand extends Command { } } - protected createOptionsFromCommandLine(inputs: CommandLineInputs, options: CommandLineOptions): IonicCapacitorOptions { + protected async createOptionsFromCommandLine(inputs: CommandLineInputs, options: CommandLineOptions): Promise { const separatedArgs = options['--']; const verbose = !!options['verbose']; - const conf = this.getCapacitorConfig(); - const server = conf.get('server'); + const conf = await this.getCapacitorConfig(); return { '--': separatedArgs ? separatedArgs : [], - appId: conf.get('appId'), - appName: conf.get('appName'), - server: { - url: server?.url, - }, verbose, + ...conf, }; } diff --git a/packages/@ionic/cli/src/commands/capacitor/build.ts b/packages/@ionic/cli/src/commands/capacitor/build.ts index dd09e19baf..b5ba3770c8 100644 --- a/packages/@ionic/cli/src/commands/capacitor/build.ts +++ b/packages/@ionic/cli/src/commands/capacitor/build.ts @@ -160,7 +160,7 @@ To configure your native project, see the common configuration docs[^capacitor-n await hook.run({ name: hook.name, build: buildRunner.createOptionsFromCommandLine(inputs, options), - capacitor: this.createOptionsFromCommandLine(inputs, options), + capacitor: await this.createOptionsFromCommandLine(inputs, options), }); } catch (e) { if (e instanceof BaseError) { diff --git a/packages/@ionic/cli/src/commands/capacitor/run.ts b/packages/@ionic/cli/src/commands/capacitor/run.ts index 69b7b8b74c..64608ca063 100644 --- a/packages/@ionic/cli/src/commands/capacitor/run.ts +++ b/packages/@ionic/cli/src/commands/capacitor/run.ts @@ -177,6 +177,8 @@ For Android and iOS, you can setup Remote Debugging on your device with browser ); } + await this.runCapacitor(['sync', platform]); + try { if (options['livereload']) { await this.runServe(inputs, options); @@ -231,11 +233,10 @@ For Android and iOS, you can setup Remote Debugging on your device with browser serverUrl = `${details.protocol || 'http'}://${details.externalAddress}:${details.port}`; } - const conf = this.getCapacitorConfig(); + const conf = await this.getGeneratedConfig(platform); onBeforeExit(async () => { conf.resetServerUrl(); - await this.runCapacitor(['copy', platform]); }); conf.setServerUrl(serverUrl); @@ -248,7 +249,6 @@ For Android and iOS, you can setup Remote Debugging on your device with browser const [ platform ] = inputs; - await this.runCapacitor(['copy', platform]); await this.runCapacitorRunHook('capacitor:run:before', inputs, options, { ...this.env, project: this.project }); if (options['open'] !== false) { @@ -279,7 +279,7 @@ For Android and iOS, you can setup Remote Debugging on your device with browser const [ platform ] = inputs; await this.runCapacitorRunHook('capacitor:run:before', inputs, options, { ...this.env, project: this.project }); - await this.runCapacitor(['run', platform, ...(typeof options['target'] === 'string' ? ['--target', options['target']] : [])]); + await this.runCapacitor(['run', platform, '--no-sync', ...(typeof options['target'] === 'string' ? ['--target', options['target']] : [])]); } protected async runCapacitorRunHook(name: CapacitorRunHookName, inputs: CommandLineInputs, options: CommandLineOptions, e: HookDeps): Promise { @@ -302,7 +302,7 @@ For Android and iOS, you can setup Remote Debugging on your device with browser name: hook.name, serve: serveOptions, build: buildOptions, - capacitor: this.createOptionsFromCommandLine(inputs, options), + capacitor: await this.createOptionsFromCommandLine(inputs, options), }); } catch (e) { if (e instanceof BaseError) { diff --git a/packages/@ionic/cli/src/commands/capacitor/sync.ts b/packages/@ionic/cli/src/commands/capacitor/sync.ts index b736cd367d..a0a8fbb265 100644 --- a/packages/@ionic/cli/src/commands/capacitor/sync.ts +++ b/packages/@ionic/cli/src/commands/capacitor/sync.ts @@ -90,7 +90,7 @@ ${input('ionic capacitor sync')} will do the following: await hook.run({ name: hook.name, build: buildRunner.createOptionsFromCommandLine(inputs, options), - capacitor: this.createOptionsFromCommandLine(inputs, options), + capacitor: await this.createOptionsFromCommandLine(inputs, options), }); } catch (e) { if (e instanceof BaseError) { diff --git a/packages/@ionic/cli/src/definitions.ts b/packages/@ionic/cli/src/definitions.ts index 249ceaee2d..241c83336d 100644 --- a/packages/@ionic/cli/src/definitions.ts +++ b/packages/@ionic/cli/src/definitions.ts @@ -20,6 +20,7 @@ import { ChildProcess, SpawnOptions } from 'child_process'; import * as fs from 'fs'; import type { Integration as CapacitorIntegration } from './lib/integrations/capacitor'; +import type { CapacitorConfigFile } from './lib/integrations/capacitor/config'; import type { Integration as CordovaIntegration } from './lib/integrations/cordova'; import type { Integration as EnterpriseIntegration } from './lib/integrations/enterprise'; @@ -653,14 +654,9 @@ export interface VueBuildOptions extends BuildOptions<'vue'> { sourcemaps?: boolean; } -export interface IonicCapacitorOptions { - verbose?: boolean; - appId?: string; - appName?: string; - server?: { - url?: string; - }; +export interface IonicCapacitorOptions extends CapacitorConfigFile { '--': string[]; + verbose?: boolean; } export interface IonicAngularBuildOptions extends BuildOptions<'ionic-angular'> { diff --git a/packages/@ionic/cli/src/lib/integrations/capacitor/config.ts b/packages/@ionic/cli/src/lib/integrations/capacitor/config.ts index 684b1dbaba..7d9cbffc7b 100644 --- a/packages/@ionic/cli/src/lib/integrations/capacitor/config.ts +++ b/packages/@ionic/cli/src/lib/integrations/capacitor/config.ts @@ -16,6 +16,10 @@ export interface CapacitorConfigFile { } export class CapacitorConfig extends BaseConfig { + constructor(p: string) { + super(p, { spaces: '\t' }); + } + provideDefaults(config: CapacitorConfigFile): CapacitorConfigFile { return config; } diff --git a/packages/@ionic/cli/src/lib/integrations/capacitor/index.ts b/packages/@ionic/cli/src/lib/integrations/capacitor/index.ts index d87fb8af2b..ba87e81b28 100644 --- a/packages/@ionic/cli/src/lib/integrations/capacitor/index.ts +++ b/packages/@ionic/cli/src/lib/integrations/capacitor/index.ts @@ -15,7 +15,21 @@ import { import { input, strong } from '../../color'; import { pkgManagerArgs } from '../../utils/npm'; -import { CAPACITOR_CONFIG_FILE, CapacitorConfig } from './config'; +import { CAPACITOR_CONFIG_FILE, CapacitorConfig, CapacitorConfigFile } from './config'; + +export interface CapacitorCLIConfig { + android: { + platformDirAbs: string; + assetsDirAbs: string; + }; + ios: { + platformDirAbs: string; + nativeTargetDirAbs: string; + }; + app: { + extConfig: CapacitorConfigFile; + } +} export class Integration extends BaseIntegration { readonly name: IntegrationName = 'capacitor'; @@ -142,4 +156,12 @@ export class Integration extends BaseIntegration { async getCapacitorCLIVersion(): Promise { return this.e.shell.cmdinfo('capacitor', ['--version'], { cwd: this.e.project.directory }); } + + async getCapacitorCLIConfig(): Promise { + const output = await this.e.shell.cmdinfo('capacitor', ['config', '--json'], { cwd: this.e.project.directory }); + + if (output) { + return JSON.parse(output); + } + } } From 0131f2ef2fab0a75443589648e28ae9d4b78f2b5 Mon Sep 17 00:00:00 2001 From: Daniel Imhoff Date: Wed, 18 Nov 2020 14:18:27 -0800 Subject: [PATCH 03/34] specific version --- packages/@ionic/cli/src/commands/capacitor/run.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/@ionic/cli/src/commands/capacitor/run.ts b/packages/@ionic/cli/src/commands/capacitor/run.ts index 64608ca063..b7e0ee3f71 100644 --- a/packages/@ionic/cli/src/commands/capacitor/run.ts +++ b/packages/@ionic/cli/src/commands/capacitor/run.ts @@ -2,6 +2,7 @@ import { BaseError, Footnote, MetadataGroup, validators } from '@ionic/cli-frame import { onBeforeExit, sleepForever } from '@ionic/utils-process'; import * as chalk from 'chalk'; import * as lodash from 'lodash'; +import * as semver from 'semver'; import { AnyBuildOptions, AnyServeOptions, CapacitorRunHookName, CommandInstanceInfo, CommandLineInputs, CommandLineOptions, CommandMetadata, CommandMetadataOption, CommandPreRun } from '../../definitions'; import { ancillary, input, strong, weak } from '../../lib/color'; @@ -167,7 +168,7 @@ For Android and iOS, you can setup Remote Debugging on your device with browser } const version = await this.getCapacitorVersion(); - const isOldCapacitor = version.major < 3; + const isOldCapacitor = semver.lt(version, '3.0.0-alpha.7'); if (isOldCapacitor) { this.env.log.warn( From 789d1654b4b513c1650d5a89643c5127c93d7629 Mon Sep 17 00:00:00 2001 From: Daniel Imhoff Date: Wed, 18 Nov 2020 16:10:08 -0800 Subject: [PATCH 04/34] android cleartext --- .../@ionic/cli/src/commands/capacitor/base.ts | 14 +++ .../@ionic/cli/src/commands/capacitor/run.ts | 11 +++ .../src/lib/integrations/capacitor/android.ts | 93 +++++++++++++++++++ .../src/lib/integrations/capacitor/index.ts | 1 + 4 files changed, 119 insertions(+) create mode 100644 packages/@ionic/cli/src/lib/integrations/capacitor/android.ts diff --git a/packages/@ionic/cli/src/commands/capacitor/base.ts b/packages/@ionic/cli/src/commands/capacitor/base.ts index f562b4bd8a..0da46e37b5 100644 --- a/packages/@ionic/cli/src/commands/capacitor/base.ts +++ b/packages/@ionic/cli/src/commands/capacitor/base.ts @@ -9,6 +9,7 @@ import { Command } from '../../lib/command'; import { FatalException, RunnerException } from '../../lib/errors'; import { runCommand } from '../../lib/executor'; import type { CapacitorCLIConfig, Integration as CapacitorIntegration } from '../../lib/integrations/capacitor' +import { ANDROID_MANIFEST_FILE, CapacitorAndroidManifest } from '../../lib/integrations/capacitor/android'; import { CAPACITOR_CONFIG_FILE, CapacitorConfig, CapacitorConfigFile } from '../../lib/integrations/capacitor/config'; import { generateOptionsForCapacitorBuild } from '../../lib/integrations/capacitor/utils'; @@ -49,6 +50,19 @@ export abstract class CapacitorCommand extends Command { return path.resolve(this.project.directory, p, CAPACITOR_CONFIG_FILE); } + async getAndroidManifest(): Promise { + const p = await this.getAndroidManifestPath(); + + return CapacitorAndroidManifest.load(p); + } + + async getAndroidManifestPath(): Promise { + const cli = await this.getCapacitorCLIConfig(); + const srcDir = cli?.android.srcDirAbs ?? 'android/app/src/main'; + + return path.resolve(this.integration.root, srcDir, ANDROID_MANIFEST_FILE); + } + async getGeneratedConfigDir(platform: string): Promise { const cli = await this.getCapacitorCLIConfig(); diff --git a/packages/@ionic/cli/src/commands/capacitor/run.ts b/packages/@ionic/cli/src/commands/capacitor/run.ts index b7e0ee3f71..f85189fdae 100644 --- a/packages/@ionic/cli/src/commands/capacitor/run.ts +++ b/packages/@ionic/cli/src/commands/capacitor/run.ts @@ -241,6 +241,17 @@ For Android and iOS, you can setup Remote Debugging on your device with browser }); conf.setServerUrl(serverUrl); + + const manifest = await this.getAndroidManifest(); + + if (platform === 'android') { + onBeforeExit(async () => { + await manifest.reset(); + }); + + manifest.enableCleartextTraffic(); + await manifest.save(); + } } protected async runCapacitorOpenFlow(inputs: CommandLineInputs, options: CommandLineOptions): Promise { diff --git a/packages/@ionic/cli/src/lib/integrations/capacitor/android.ts b/packages/@ionic/cli/src/lib/integrations/capacitor/android.ts new file mode 100644 index 0000000000..f0ece072d9 --- /dev/null +++ b/packages/@ionic/cli/src/lib/integrations/capacitor/android.ts @@ -0,0 +1,93 @@ +import { readFile, writeFile, unlink } from '@ionic/utils-fs'; +import * as et from 'elementtree'; + +export const ANDROID_MANIFEST_FILE = 'AndroidManifest.xml'; + +export class CapacitorAndroidManifest { + protected _doc?: et.ElementTree; + protected origManifestContent?: string; + protected saving = false; + + constructor(readonly manifestPath: string) {} + + get origManifestPath(): string { + return `${this.manifestPath}.orig`; + } + + get doc(): et.ElementTree { + if (!this._doc) { + throw new Error('No doc loaded.'); + } + + return this._doc; + } + + static async load(manifestPath: string): Promise { + if (!manifestPath) { + throw new Error(`Must supply file path for ${ANDROID_MANIFEST_FILE}.`); + } + + const conf = new CapacitorAndroidManifest(manifestPath); + await conf.reload(); + + return conf; + } + + protected async reload(): Promise { + this.origManifestContent = await readFile(this.manifestPath, { encoding: 'utf8' }); + + try { + this._doc = et.parse(this.origManifestContent); + } catch (e) { + throw new Error(`Cannot parse ${ANDROID_MANIFEST_FILE} file: ${e.stack ?? e}`); + } + } + + enableCleartextTraffic() { + const node = this.getApplicationNode(); + node.set('android:usesCleartextTraffic', 'true'); + } + + async reset(): Promise { + const origManifestContent = await readFile(this.origManifestPath, { encoding: 'utf8' }); + + if (!this.saving) { + this.saving = true; + await writeFile(this.manifestPath, origManifestContent, { encoding: 'utf8' }); + await unlink(this.origManifestPath); + this.saving = false; + } + } + + async save(): Promise { + if (!this.saving) { + this.saving = true; + + if (this.origManifestContent) { + await writeFile(this.origManifestPath, this.origManifestContent, { encoding: 'utf8' }); + this.origManifestContent = undefined; + } + + await writeFile(this.manifestPath, this.write(), { encoding: 'utf8' }); + + this.saving = false; + } + } + + protected getApplicationNode(): et.Element { + const root = this.doc.getroot(); + const applicationNode = root.find('application'); + + if (!applicationNode) { + throw new Error(`No node in ${ANDROID_MANIFEST_FILE}.`); + } + + return applicationNode; + } + + protected write(): string { + const contents = this.doc.write({ indent: 4 }); + + return contents; + } +} diff --git a/packages/@ionic/cli/src/lib/integrations/capacitor/index.ts b/packages/@ionic/cli/src/lib/integrations/capacitor/index.ts index ba87e81b28..061311535b 100644 --- a/packages/@ionic/cli/src/lib/integrations/capacitor/index.ts +++ b/packages/@ionic/cli/src/lib/integrations/capacitor/index.ts @@ -20,6 +20,7 @@ import { CAPACITOR_CONFIG_FILE, CapacitorConfig, CapacitorConfigFile } from './c export interface CapacitorCLIConfig { android: { platformDirAbs: string; + srcDirAbs: string; assetsDirAbs: string; }; ios: { From 65129fae624b45c0c84c6b66406abe4f0b3466a4 Mon Sep 17 00:00:00 2001 From: Daniel Imhoff Date: Wed, 18 Nov 2020 16:10:27 -0800 Subject: [PATCH 05/34] use integration root --- packages/@ionic/cli/src/commands/capacitor/base.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/@ionic/cli/src/commands/capacitor/base.ts b/packages/@ionic/cli/src/commands/capacitor/base.ts index 0da46e37b5..d3c4d0f1d0 100644 --- a/packages/@ionic/cli/src/commands/capacitor/base.ts +++ b/packages/@ionic/cli/src/commands/capacitor/base.ts @@ -47,7 +47,7 @@ export abstract class CapacitorCommand extends Command { const p = await this.getGeneratedConfigDir(platform); - return path.resolve(this.project.directory, p, CAPACITOR_CONFIG_FILE); + return path.resolve(this.integration.root, p, CAPACITOR_CONFIG_FILE); } async getAndroidManifest(): Promise { @@ -88,12 +88,12 @@ export abstract class CapacitorCommand extends Command { return; } - try { + const configPath = path.resolve(this.integration.root, CAPACITOR_CONFIG_FILE); + // fallback to reading capacitor.config.json if it exists - const conf = new CapacitorConfig(path.resolve(this.project.directory, CAPACITOR_CONFIG_FILE)); + if (await pathExists(configPath)) { + const conf = new CapacitorConfig(configPath); return conf.c; - } catch (e) { - // ignore } } @@ -263,7 +263,7 @@ export abstract class CapacitorCommand extends Command { return false; } - await this.env.shell.run(manager, managerArgs, { cwd: this.project.directory }); + await this.env.shell.run(manager, managerArgs, { cwd: this.integration.root }); return true; } From 180b1fb897a51e23e30b02aa7d26d91fe31bd55d Mon Sep 17 00:00:00 2001 From: Daniel Imhoff Date: Wed, 18 Nov 2020 16:28:36 -0800 Subject: [PATCH 06/34] wip --- .../@ionic/cli/src/commands/capacitor/base.ts | 26 ++------------ .../src/lib/integrations/capacitor/index.ts | 35 ++++++++++++------- packages/@ionic/cli/src/lib/project/index.ts | 9 ++--- 3 files changed, 31 insertions(+), 39 deletions(-) diff --git a/packages/@ionic/cli/src/commands/capacitor/base.ts b/packages/@ionic/cli/src/commands/capacitor/base.ts index d3c4d0f1d0..9a1bbdd93a 100644 --- a/packages/@ionic/cli/src/commands/capacitor/base.ts +++ b/packages/@ionic/cli/src/commands/capacitor/base.ts @@ -10,7 +10,7 @@ import { FatalException, RunnerException } from '../../lib/errors'; import { runCommand } from '../../lib/executor'; import type { CapacitorCLIConfig, Integration as CapacitorIntegration } from '../../lib/integrations/capacitor' import { ANDROID_MANIFEST_FILE, CapacitorAndroidManifest } from '../../lib/integrations/capacitor/android'; -import { CAPACITOR_CONFIG_FILE, CapacitorConfig, CapacitorConfigFile } from '../../lib/integrations/capacitor/config'; +import { CAPACITOR_CONFIG_FILE, CapacitorConfig } from '../../lib/integrations/capacitor/config'; import { generateOptionsForCapacitorBuild } from '../../lib/integrations/capacitor/utils'; export abstract class CapacitorCommand extends Command { @@ -76,27 +76,6 @@ export abstract class CapacitorCommand extends Command { throw new FatalException(`Could not determine generated Capacitor config path for ${input(platform)} platform.`); } - async getCapacitorConfig(): Promise { - // try using `capacitor config --json` - const cli = await this.getCapacitorCLIConfig(); - - if (cli) { - return cli.app.extConfig; - } - - if (!this.project) { - return; - } - - const configPath = path.resolve(this.integration.root, CAPACITOR_CONFIG_FILE); - - // fallback to reading capacitor.config.json if it exists - if (await pathExists(configPath)) { - const conf = new CapacitorConfig(configPath); - return conf.c; - } - } - async getCapacitorCLIConfig(): Promise { if (!this._cliconfig) { const capacitor = await this.getCapacitorIntegration(); @@ -233,7 +212,8 @@ export abstract class CapacitorCommand extends Command { protected async createOptionsFromCommandLine(inputs: CommandLineInputs, options: CommandLineOptions): Promise { const separatedArgs = options['--']; const verbose = !!options['verbose']; - const conf = await this.getCapacitorConfig(); + const capacitor = await this.getCapacitorIntegration(); + const conf = await capacitor.getCapacitorConfig(); return { '--': separatedArgs ? separatedArgs : [], diff --git a/packages/@ionic/cli/src/lib/integrations/capacitor/index.ts b/packages/@ionic/cli/src/lib/integrations/capacitor/index.ts index 061311535b..11b8993980 100644 --- a/packages/@ionic/cli/src/lib/integrations/capacitor/index.ts +++ b/packages/@ionic/cli/src/lib/integrations/capacitor/index.ts @@ -42,7 +42,7 @@ export class Integration extends BaseIntegration { } async add(details: IntegrationAddDetails): Promise { - const confPath = this.getCapacitorConfigPath(); + const confPath = this.getCapacitorConfigJsonPath(); if (await pathExists(confPath)) { this.e.log.nl(); @@ -85,15 +85,8 @@ export class Integration extends BaseIntegration { await super.add(details); } - async getCapacitorConfig(): Promise { - const confPath = this.getCapacitorConfigPath(); - const conf = new CapacitorConfig(confPath); - - return conf; - } - - getCapacitorConfigPath(): string { - return path.resolve(this.e.project.directory, CAPACITOR_CONFIG_FILE); + protected getCapacitorConfigJsonPath(): string { + return path.resolve(this.config.get('root', this.e.project.directory), CAPACITOR_CONFIG_FILE); } async installCapacitorCore() { @@ -107,7 +100,8 @@ export class Integration extends BaseIntegration { } async personalize({ name, packageId }: ProjectPersonalizationDetails) { - const conf = await this.getCapacitorConfig(); + const confPath = this.getCapacitorConfigJsonPath(); + const conf = new CapacitorConfig(confPath); conf.set('appName', name); @@ -118,7 +112,7 @@ export class Integration extends BaseIntegration { async getInfo(): Promise { const conf = await this.getCapacitorConfig(); - const bundleId = conf.get('appId'); + const bundleId = conf?.appId; const [ [ capacitorCorePkg, capacitorCorePkgPath ], @@ -165,4 +159,21 @@ export class Integration extends BaseIntegration { return JSON.parse(output); } } + + async getCapacitorConfig(): Promise { + // try using `capacitor config --json` + const cli = await this.getCapacitorCLIConfig(); + + if (cli) { + return cli.app.extConfig; + } + + const confPath = this.getCapacitorConfigJsonPath(); + + // fallback to reading capacitor.config.json if it exists + if (await pathExists(confPath)) { + const conf = new CapacitorConfig(confPath); + return conf.c; + } + } } diff --git a/packages/@ionic/cli/src/lib/project/index.ts b/packages/@ionic/cli/src/lib/project/index.ts index 86515add7a..7c101081f6 100644 --- a/packages/@ionic/cli/src/lib/project/index.ts +++ b/packages/@ionic/cli/src/lib/project/index.ts @@ -14,7 +14,6 @@ import { isMultiProjectConfig, isProjectConfig } from '../../guards'; import { ancillary, failure, input, strong } from '../color'; import { BaseException, FatalException, IntegrationNotFoundException, RunnerNotFoundException } from '../errors'; import { BaseIntegration } from '../integrations'; -import { CAPACITOR_CONFIG_FILE, CapacitorConfig } from '../integrations/capacitor/config'; import { Color } from '../utils/color'; import type { Integration as CapacitorIntegration } from '../integrations/capacitor'; import type { Integration as CordovaIntegration } from '../integrations/cordova'; @@ -549,13 +548,15 @@ export abstract class Project implements IProject { async getDistDir(): Promise { if (this.getIntegration('capacitor') !== undefined) { - const conf = new CapacitorConfig(path.resolve(this.directory, CAPACITOR_CONFIG_FILE)); - const webDir = conf.get('webDir'); + const capacitor = await this.createIntegration('capacitor'); + const conf = await capacitor.getCapacitorCLIConfig(); + const webDir = conf?.app.extConfig.webDir; + if (webDir) { return path.resolve(this.directory, webDir); } else { throw new FatalException( - `The ${input('webDir')} property must be set in the Capacitor configuration file (${input(CAPACITOR_CONFIG_FILE)}). \n` + + `The ${input('webDir')} property must be set in the Capacitor configuration file. \n` + `See the Capacitor docs for more information: ${strong('https://capacitor.ionicframework.com/docs/basics/configuring-your-app')}` ); } From 13d50d91050face5e6cd4424e91e00ed6a4b92d0 Mon Sep 17 00:00:00 2001 From: Daniel Imhoff Date: Wed, 18 Nov 2020 16:36:28 -0800 Subject: [PATCH 07/34] better names --- packages/@ionic/cli/src/commands/capacitor/base.ts | 12 ++++++------ packages/@ionic/cli/src/definitions.ts | 4 ++-- .../cli/src/lib/integrations/capacitor/config.ts | 10 ++++------ .../cli/src/lib/integrations/capacitor/index.ts | 12 ++++++------ 4 files changed, 18 insertions(+), 20 deletions(-) diff --git a/packages/@ionic/cli/src/commands/capacitor/base.ts b/packages/@ionic/cli/src/commands/capacitor/base.ts index 9a1bbdd93a..c1ff144781 100644 --- a/packages/@ionic/cli/src/commands/capacitor/base.ts +++ b/packages/@ionic/cli/src/commands/capacitor/base.ts @@ -4,13 +4,13 @@ import * as path from 'path'; import * as semver from 'semver'; import { CommandInstanceInfo, CommandLineInputs, CommandLineOptions, IonicCapacitorOptions, ProjectIntegration } from '../../definitions'; -import { input, strong } from '../../lib/color'; +import { input } from '../../lib/color'; import { Command } from '../../lib/command'; import { FatalException, RunnerException } from '../../lib/errors'; import { runCommand } from '../../lib/executor'; import type { CapacitorCLIConfig, Integration as CapacitorIntegration } from '../../lib/integrations/capacitor' import { ANDROID_MANIFEST_FILE, CapacitorAndroidManifest } from '../../lib/integrations/capacitor/android'; -import { CAPACITOR_CONFIG_FILE, CapacitorConfig } from '../../lib/integrations/capacitor/config'; +import { CAPACITOR_CONFIG_JSON_FILE, CapacitorJSONConfig } from '../../lib/integrations/capacitor/config'; import { generateOptionsForCapacitorBuild } from '../../lib/integrations/capacitor/utils'; export abstract class CapacitorCommand extends Command { @@ -30,14 +30,14 @@ export abstract class CapacitorCommand extends Command { return this._integration; } - async getGeneratedConfig(platform: string): Promise { + async getGeneratedConfig(platform: string): Promise { if (!this.project) { throw new FatalException(`Cannot use Capacitor outside a project directory.`); } const p = await this.getGeneratedConfigPath(platform); - return new CapacitorConfig(p); + return new CapacitorJSONConfig(p); } async getGeneratedConfigPath(platform: string): Promise { @@ -47,7 +47,7 @@ export abstract class CapacitorCommand extends Command { const p = await this.getGeneratedConfigDir(platform); - return path.resolve(this.integration.root, p, CAPACITOR_CONFIG_FILE); + return path.resolve(this.integration.root, p, CAPACITOR_CONFIG_JSON_FILE); } async getAndroidManifest(): Promise { @@ -165,7 +165,7 @@ export abstract class CapacitorCommand extends Command { this.env.log.warn( `Capacitor server URL is in use.\n` + `This may result in unexpected behavior for this build, where an external server is used in the Web View instead of your app. This likely occurred because of ${input('--livereload')} usage in the past and the CLI improperly exiting without cleaning up.\n\n` + - `Delete the ${input('server')} key in the ${strong(CAPACITOR_CONFIG_FILE)} file if you did not intend to use an external server.` + `Delete the ${input('server')} key in the Capacitor config file if you did not intend to use an external server.` ); this.env.log.nl(); } diff --git a/packages/@ionic/cli/src/definitions.ts b/packages/@ionic/cli/src/definitions.ts index 241c83336d..17d90e38c7 100644 --- a/packages/@ionic/cli/src/definitions.ts +++ b/packages/@ionic/cli/src/definitions.ts @@ -20,7 +20,7 @@ import { ChildProcess, SpawnOptions } from 'child_process'; import * as fs from 'fs'; import type { Integration as CapacitorIntegration } from './lib/integrations/capacitor'; -import type { CapacitorConfigFile } from './lib/integrations/capacitor/config'; +import type { CapacitorConfig } from './lib/integrations/capacitor/config'; import type { Integration as CordovaIntegration } from './lib/integrations/cordova'; import type { Integration as EnterpriseIntegration } from './lib/integrations/enterprise'; @@ -654,7 +654,7 @@ export interface VueBuildOptions extends BuildOptions<'vue'> { sourcemaps?: boolean; } -export interface IonicCapacitorOptions extends CapacitorConfigFile { +export interface IonicCapacitorOptions extends CapacitorConfig { '--': string[]; verbose?: boolean; } diff --git a/packages/@ionic/cli/src/lib/integrations/capacitor/config.ts b/packages/@ionic/cli/src/lib/integrations/capacitor/config.ts index 7d9cbffc7b..c44adecd65 100644 --- a/packages/@ionic/cli/src/lib/integrations/capacitor/config.ts +++ b/packages/@ionic/cli/src/lib/integrations/capacitor/config.ts @@ -1,26 +1,24 @@ import { BaseConfig } from '@ionic/cli-framework'; import * as lodash from 'lodash'; -export const CAPACITOR_CONFIG_FILE = 'capacitor.config.json'; +export const CAPACITOR_CONFIG_JSON_FILE = 'capacitor.config.json'; -export interface CapacitorConfigFile { +export interface CapacitorConfig { appId?: string; appName?: string; webDir?: string; server?: { - cleartext?: boolean; url?: string; - originalCleartext?: boolean; originalUrl?: string; }; } -export class CapacitorConfig extends BaseConfig { +export class CapacitorJSONConfig extends BaseConfig { constructor(p: string) { super(p, { spaces: '\t' }); } - provideDefaults(config: CapacitorConfigFile): CapacitorConfigFile { + provideDefaults(config: CapacitorConfig): CapacitorConfig { return config; } diff --git a/packages/@ionic/cli/src/lib/integrations/capacitor/index.ts b/packages/@ionic/cli/src/lib/integrations/capacitor/index.ts index 11b8993980..33c29be754 100644 --- a/packages/@ionic/cli/src/lib/integrations/capacitor/index.ts +++ b/packages/@ionic/cli/src/lib/integrations/capacitor/index.ts @@ -15,7 +15,7 @@ import { import { input, strong } from '../../color'; import { pkgManagerArgs } from '../../utils/npm'; -import { CAPACITOR_CONFIG_FILE, CapacitorConfig, CapacitorConfigFile } from './config'; +import { CapacitorJSONConfig, CapacitorConfig } from './config'; export interface CapacitorCLIConfig { android: { @@ -28,7 +28,7 @@ export interface CapacitorCLIConfig { nativeTargetDirAbs: string; }; app: { - extConfig: CapacitorConfigFile; + extConfig: CapacitorConfig; } } @@ -86,7 +86,7 @@ export class Integration extends BaseIntegration { } protected getCapacitorConfigJsonPath(): string { - return path.resolve(this.config.get('root', this.e.project.directory), CAPACITOR_CONFIG_FILE); + return path.resolve(this.config.get('root', this.e.project.directory), 'capacitor.config.json'); } async installCapacitorCore() { @@ -101,7 +101,7 @@ export class Integration extends BaseIntegration { async personalize({ name, packageId }: ProjectPersonalizationDetails) { const confPath = this.getCapacitorConfigJsonPath(); - const conf = new CapacitorConfig(confPath); + const conf = new CapacitorJSONConfig(confPath); conf.set('appName', name); @@ -160,7 +160,7 @@ export class Integration extends BaseIntegration { } } - async getCapacitorConfig(): Promise { + async getCapacitorConfig(): Promise { // try using `capacitor config --json` const cli = await this.getCapacitorCLIConfig(); @@ -172,7 +172,7 @@ export class Integration extends BaseIntegration { // fallback to reading capacitor.config.json if it exists if (await pathExists(confPath)) { - const conf = new CapacitorConfig(confPath); + const conf = new CapacitorJSONConfig(confPath); return conf.c; } } From 65d877967bf4a0686c988b715bfd008ad353020d Mon Sep 17 00:00:00 2001 From: Daniel Imhoff Date: Wed, 18 Nov 2020 16:36:48 -0800 Subject: [PATCH 08/34] cleartext not necessary in generated config --- .../cli/src/lib/integrations/capacitor/config.ts | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/packages/@ionic/cli/src/lib/integrations/capacitor/config.ts b/packages/@ionic/cli/src/lib/integrations/capacitor/config.ts index c44adecd65..8cb98b21ce 100644 --- a/packages/@ionic/cli/src/lib/integrations/capacitor/config.ts +++ b/packages/@ionic/cli/src/lib/integrations/capacitor/config.ts @@ -29,12 +29,7 @@ export class CapacitorJSONConfig extends BaseConfig { serverConfig.originalUrl = serverConfig.url; } - if (typeof serverConfig.cleartext === 'boolean') { - serverConfig.originalCleartext = serverConfig.cleartext; - } - serverConfig.url = url; - serverConfig.cleartext = true; this.set('server', serverConfig); } @@ -43,18 +38,12 @@ export class CapacitorJSONConfig extends BaseConfig { const serverConfig = this.get('server') || {}; delete serverConfig.url; - delete serverConfig.cleartext; if (serverConfig.originalUrl) { serverConfig.url = serverConfig.originalUrl; delete serverConfig.originalUrl; } - if (serverConfig.originalCleartext) { - serverConfig.cleartext = serverConfig.originalCleartext; - delete serverConfig.originalCleartext; - } - if (lodash.isEmpty(serverConfig)) { this.unset('server'); } else { From 5b66a3e1cbbc797feb1def02ef6194a3297e3f20 Mon Sep 17 00:00:00 2001 From: Daniel Imhoff Date: Thu, 19 Nov 2020 09:57:21 -0800 Subject: [PATCH 09/34] use srcMainDir --- packages/@ionic/cli/src/commands/capacitor/base.ts | 2 +- packages/@ionic/cli/src/lib/integrations/capacitor/index.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/@ionic/cli/src/commands/capacitor/base.ts b/packages/@ionic/cli/src/commands/capacitor/base.ts index c1ff144781..abf65f2f4e 100644 --- a/packages/@ionic/cli/src/commands/capacitor/base.ts +++ b/packages/@ionic/cli/src/commands/capacitor/base.ts @@ -58,7 +58,7 @@ export abstract class CapacitorCommand extends Command { async getAndroidManifestPath(): Promise { const cli = await this.getCapacitorCLIConfig(); - const srcDir = cli?.android.srcDirAbs ?? 'android/app/src/main'; + const srcDir = cli?.android.srcMainDirAbs ?? 'android/app/src/main'; return path.resolve(this.integration.root, srcDir, ANDROID_MANIFEST_FILE); } diff --git a/packages/@ionic/cli/src/lib/integrations/capacitor/index.ts b/packages/@ionic/cli/src/lib/integrations/capacitor/index.ts index 33c29be754..e68624f683 100644 --- a/packages/@ionic/cli/src/lib/integrations/capacitor/index.ts +++ b/packages/@ionic/cli/src/lib/integrations/capacitor/index.ts @@ -20,7 +20,7 @@ import { CapacitorJSONConfig, CapacitorConfig } from './config'; export interface CapacitorCLIConfig { android: { platformDirAbs: string; - srcDirAbs: string; + srcMainDirAbs: string; assetsDirAbs: string; }; ios: { From caedb8087b82eb1587bcbf673f1f50637ce2bd1a Mon Sep 17 00:00:00 2001 From: Daniel Imhoff Date: Thu, 19 Nov 2020 11:35:47 -0800 Subject: [PATCH 10/34] redundant message --- packages/@ionic/cli/src/commands/capacitor/run.ts | 6 ------ 1 file changed, 6 deletions(-) diff --git a/packages/@ionic/cli/src/commands/capacitor/run.ts b/packages/@ionic/cli/src/commands/capacitor/run.ts index f85189fdae..c05c9cf847 100644 --- a/packages/@ionic/cli/src/commands/capacitor/run.ts +++ b/packages/@ionic/cli/src/commands/capacitor/run.ts @@ -225,12 +225,6 @@ For Android and iOS, you can setup Remote Debugging on your device with browser if (!serverUrl) { const details = await runner.run(runnerOpts); - - if (details.externallyAccessible === false) { - const extra = LOCAL_ADDRESSES.includes(details.externalAddress) ? '\nEnsure you have proper port forwarding setup from your device to your computer.' : ''; - this.env.log.warn(`Your device or emulator may not be able to access ${strong(details.externalAddress)}.${extra}\n\n`); - } - serverUrl = `${details.protocol || 'http'}://${details.externalAddress}:${details.port}`; } From e065e464dcffd5f36d6fa60010e42763d16c7519 Mon Sep 17 00:00:00 2001 From: Daniel Imhoff Date: Thu, 19 Nov 2020 11:36:31 -0800 Subject: [PATCH 11/34] optomize in integration itself --- .../@ionic/cli/src/commands/capacitor/base.ts | 8 +-- .../src/lib/integrations/capacitor/index.ts | 59 +++++++++++++------ 2 files changed, 43 insertions(+), 24 deletions(-) diff --git a/packages/@ionic/cli/src/commands/capacitor/base.ts b/packages/@ionic/cli/src/commands/capacitor/base.ts index abf65f2f4e..54cceeae38 100644 --- a/packages/@ionic/cli/src/commands/capacitor/base.ts +++ b/packages/@ionic/cli/src/commands/capacitor/base.ts @@ -16,7 +16,6 @@ import { generateOptionsForCapacitorBuild } from '../../lib/integrations/capacit export abstract class CapacitorCommand extends Command { private _integration?: Required; private _integrationObject?: CapacitorIntegration; - private _cliconfig?: CapacitorCLIConfig; get integration(): Required { if (!this.project) { @@ -77,12 +76,9 @@ export abstract class CapacitorCommand extends Command { } async getCapacitorCLIConfig(): Promise { - if (!this._cliconfig) { - const capacitor = await this.getCapacitorIntegration(); - this._cliconfig = await capacitor.getCapacitorCLIConfig(); - } + const capacitor = await this.getCapacitorIntegration(); - return this._cliconfig; + return capacitor.getCapacitorCLIConfig(); } async getCapacitorIntegration() { diff --git a/packages/@ionic/cli/src/lib/integrations/capacitor/index.ts b/packages/@ionic/cli/src/lib/integrations/capacitor/index.ts index e68624f683..3dba79fe2e 100644 --- a/packages/@ionic/cli/src/lib/integrations/capacitor/index.ts +++ b/packages/@ionic/cli/src/lib/integrations/capacitor/index.ts @@ -2,8 +2,12 @@ import { PackageJson, parseArgs } from '@ionic/cli-framework'; import { mkdirp, pathExists } from '@ionic/utils-fs'; import { prettyPath } from '@ionic/utils-terminal'; import * as chalk from 'chalk'; +import * as Debug from 'debug'; +import * as lodash from 'lodash'; import * as path from 'path'; +const debug = Debug('ionic:lib:integrations:capacitor'); + import { BaseIntegration, IntegrationConfig } from '../'; import { InfoItem, @@ -41,6 +45,10 @@ export class Integration extends BaseIntegration { return new IntegrationConfig(this.e.project.filePath, { pathPrefix: [...this.e.project.pathPrefix, 'integrations', this.name] }); } + get root(): string { + return this.config.get('root', this.e.project.directory); + } + async add(details: IntegrationAddDetails): Promise { const confPath = this.getCapacitorConfigJsonPath(); @@ -86,17 +94,17 @@ export class Integration extends BaseIntegration { } protected getCapacitorConfigJsonPath(): string { - return path.resolve(this.config.get('root', this.e.project.directory), 'capacitor.config.json'); + return path.resolve(this.root, 'capacitor.config.json'); } async installCapacitorCore() { const [ manager, ...managerArgs ] = await pkgManagerArgs(this.e.config.get('npmClient'), { command: 'install', pkg: '@capacitor/core' }); - await this.e.shell.run(manager, managerArgs, { cwd: this.e.project.directory }); + await this.e.shell.run(manager, managerArgs, { cwd: this.root }); } async installCapacitorCLI() { const [ manager, ...managerArgs ] = await pkgManagerArgs(this.e.config.get('npmClient'), { command: 'install', pkg: '@capacitor/cli', saveDev: true }); - await this.e.shell.run(manager, managerArgs, { cwd: this.e.project.directory }); + await this.e.shell.run(manager, managerArgs, { cwd: this.root }); } async personalize({ name, packageId }: ProjectPersonalizationDetails) { @@ -148,32 +156,47 @@ export class Integration extends BaseIntegration { return info; } - async getCapacitorCLIVersion(): Promise { - return this.e.shell.cmdinfo('capacitor', ['--version'], { cwd: this.e.project.directory }); - } + getCapacitorCLIVersion = lodash.memoize(async (): Promise => { + return this.e.shell.cmdinfo('capacitor', ['--version'], { cwd: this.root }); + }); - async getCapacitorCLIConfig(): Promise { - const output = await this.e.shell.cmdinfo('capacitor', ['config', '--json'], { cwd: this.e.project.directory }); + getCapacitorCLIConfig = lodash.memoize(async (): Promise => { + const args = ['config', '--json']; - if (output) { - return JSON.parse(output); + debug('Getting config with Capacitor CLI: %O', args); + + const output = await this.e.shell.cmdinfo('capacitor', args, { cwd: this.root }); + + if (!output) { + debug('Could not get config from Capacitor CLI (probably old version)'); + return; } - } - async getCapacitorConfig(): Promise { - // try using `capacitor config --json` + return JSON.parse(output); + }); + + getCapacitorConfig = lodash.memoize(async (): Promise => { const cli = await this.getCapacitorCLIConfig(); if (cli) { + debug('Loaded Capacitor config!'); return cli.app.extConfig; } + // fallback to reading capacitor.config.json if it exists const confPath = this.getCapacitorConfigJsonPath(); - // fallback to reading capacitor.config.json if it exists - if (await pathExists(confPath)) { - const conf = new CapacitorJSONConfig(confPath); - return conf.c; + if (!(await pathExists(confPath))) { + debug('Capacitor config file does not exist at %O', confPath); + debug('Failed to load Capacitor config'); + return; } - } + + const conf = new CapacitorJSONConfig(confPath); + const extConfig = conf.c; + + debug('Loaded Capacitor config!'); + + return extConfig; + }); } From 7046c689077c5bc5018e35b2568a687e726de75b Mon Sep 17 00:00:00 2001 From: Daniel Imhoff Date: Thu, 19 Nov 2020 11:56:54 -0800 Subject: [PATCH 12/34] print targets with Ionic CLI --- .../@ionic/cli/src/commands/capacitor/run.ts | 48 ++++++++++++++++--- 1 file changed, 41 insertions(+), 7 deletions(-) diff --git a/packages/@ionic/cli/src/commands/capacitor/run.ts b/packages/@ionic/cli/src/commands/capacitor/run.ts index c05c9cf847..f9834c12c3 100644 --- a/packages/@ionic/cli/src/commands/capacitor/run.ts +++ b/packages/@ionic/cli/src/commands/capacitor/run.ts @@ -1,18 +1,29 @@ import { BaseError, Footnote, MetadataGroup, validators } from '@ionic/cli-framework'; import { onBeforeExit, sleepForever } from '@ionic/utils-process'; +import { columnar } from '@ionic/utils-terminal'; import * as chalk from 'chalk'; +import * as Debug from 'debug'; import * as lodash from 'lodash'; import * as semver from 'semver'; +import { COLUMNAR_OPTIONS } from '../../constants'; import { AnyBuildOptions, AnyServeOptions, CapacitorRunHookName, CommandInstanceInfo, CommandLineInputs, CommandLineOptions, CommandMetadata, CommandMetadataOption, CommandPreRun } from '../../definitions'; import { ancillary, input, strong, weak } from '../../lib/color'; import { FatalException, RunnerException } from '../../lib/errors'; import { Hook, HookDeps } from '../../lib/hooks'; import { generateOptionsForCapacitorBuild, getNativeIDEForPlatform, getVirtualDeviceNameForPlatform } from '../../lib/integrations/capacitor/utils'; -import { COMMON_SERVE_COMMAND_OPTIONS, LOCAL_ADDRESSES } from '../../lib/serve'; +import { COMMON_SERVE_COMMAND_OPTIONS } from '../../lib/serve'; import { CapacitorCommand } from './base'; +const debug = Debug('ionic:commands:capacitor:run'); + +interface NativeTarget { + id: string; + name: string; + api: string; +} + export class RunCommand extends CapacitorCommand implements CommandPreRun { async getMetadata(): Promise { const groups: string[] = [MetadataGroup.BETA]; @@ -162,13 +173,9 @@ For Android and iOS, you can setup Remote Debugging on your device with browser const [ platform ] = inputs; - if (options['list']) { - await this.runCapacitor(['run', platform, '--list']); - throw new FatalException('', 0); - } - const version = await this.getCapacitorVersion(); const isOldCapacitor = semver.lt(version, '3.0.0-alpha.7'); + const doOpenFlow = isOldCapacitor || options['open'] === true; if (isOldCapacitor) { this.env.log.warn( @@ -178,6 +185,15 @@ For Android and iOS, you can setup Remote Debugging on your device with browser ); } + const targets = doOpenFlow ? [] : await this.getNativeTargets(platform); + + if (options['list']) { + const rows = targets.map(t => [t.name, t.api, t.id]); + this.env.log.msg(columnar(rows, { ...COLUMNAR_OPTIONS, headers: ['Name', 'API', 'Target ID'] })); + + throw new FatalException('', 0); + } + await this.runCapacitor(['sync', platform]); try { @@ -194,7 +210,7 @@ For Android and iOS, you can setup Remote Debugging on your device with browser throw e; } - if (isOldCapacitor || options['open'] === true) { + if (doOpenFlow) { await this.runCapacitorOpenFlow(inputs, options); } else { await this.runCapacitorRunFlow(inputs, options); @@ -288,6 +304,24 @@ For Android and iOS, you can setup Remote Debugging on your device with browser await this.runCapacitor(['run', platform, '--no-sync', ...(typeof options['target'] === 'string' ? ['--target', options['target']] : [])]); } + protected async getNativeTargets(platform: string): Promise { + const args = ['run', platform, '--list', '--json']; + + debug('Getting native targets for %O with Capacitor CLI: %O', platform, args); + + const output = await this.env.shell.cmdinfo('capacitor', args, { cwd: this.integration.root }); + + if (!output) { + return []; + } + + const targets = JSON.parse(output); + + debug('%O native targets found', targets.length); + + return targets; + } + protected async runCapacitorRunHook(name: CapacitorRunHookName, inputs: CommandLineInputs, options: CommandLineOptions, e: HookDeps): Promise { const hook = new CapacitorRunHook(name, e); let serveOptions: AnyServeOptions | undefined; From 6aabcabfc8854e46b9dd2bd396c3d67bf57510ed Mon Sep 17 00:00:00 2001 From: Daniel Imhoff Date: Thu, 19 Nov 2020 11:57:25 -0800 Subject: [PATCH 13/34] cleanup --- .../@ionic/cli/src/commands/capacitor/base.ts | 81 +++---------------- 1 file changed, 10 insertions(+), 71 deletions(-) diff --git a/packages/@ionic/cli/src/commands/capacitor/base.ts b/packages/@ionic/cli/src/commands/capacitor/base.ts index 54cceeae38..61cf4d0b87 100644 --- a/packages/@ionic/cli/src/commands/capacitor/base.ts +++ b/packages/@ionic/cli/src/commands/capacitor/base.ts @@ -1,5 +1,5 @@ import { pathExists } from '@ionic/utils-fs'; -import { ERROR_COMMAND_NOT_FOUND, ERROR_SIGNAL_EXIT, SubprocessError } from '@ionic/utils-subprocess'; +import * as lodash from 'lodash'; import * as path from 'path'; import * as semver from 'semver'; @@ -15,7 +15,6 @@ import { generateOptionsForCapacitorBuild } from '../../lib/integrations/capacit export abstract class CapacitorCommand extends Command { private _integration?: Required; - private _integrationObject?: CapacitorIntegration; get integration(): Required { if (!this.project) { @@ -81,17 +80,13 @@ export abstract class CapacitorCommand extends Command { return capacitor.getCapacitorCLIConfig(); } - async getCapacitorIntegration() { + getCapacitorIntegration = lodash.memoize(async (): Promise => { if (!this.project) { throw new FatalException(`Cannot use Capacitor outside a project directory.`); } - if (!this._integrationObject) { - this._integrationObject = await this.project.createIntegration('capacitor'); - } - - return this._integrationObject; - } + return this.project.createIntegration('capacitor'); + }); async getCapacitorVersion(): Promise { const capacitor = await this.getCapacitorIntegration(); @@ -120,34 +115,12 @@ export abstract class CapacitorCommand extends Command { await this.checkCapacitor(runinfo); } - async runCapacitor(argList: string[]): Promise { - try { - return await this._runCapacitor(argList); - } catch (e) { - if (e instanceof SubprocessError) { - if (e.code === ERROR_COMMAND_NOT_FOUND) { - const pkg = '@capacitor/cli'; - const requiredMsg = `The Capacitor CLI is required for Capacitor projects.`; - this.env.log.nl(); - this.env.log.info(`Looks like ${input(pkg)} isn't installed in this project.\n` + requiredMsg); - this.env.log.nl(); - - const installed = await this.promptToInstallCapacitor(); - - if (!installed) { - throw new FatalException(`${input(pkg)} is required for Capacitor projects.`); - } - - return this.runCapacitor(argList); - } - - if (e.code === ERROR_SIGNAL_EXIT) { - return; - } - } - - throw e; + async runCapacitor(argList: string[]) { + if (!this.project) { + throw new FatalException(`Cannot use Capacitor outside a project directory.`); } + + await this.env.shell.run('capacitor', argList, { fatalOnNotFound: false, truncateErrorOutput: 5000, stdio: 'inherit', cwd: this.integration.root }); } async runBuild(inputs: CommandLineInputs, options: CommandLineOptions): Promise { @@ -200,7 +173,7 @@ export abstract class CapacitorCommand extends Command { .map(([p]) => p); if (!platforms.includes(platform)) { - await this._runCapacitor(['add', platform]); + await this.runCapacitor(['add', platform]); } } } @@ -217,38 +190,4 @@ export abstract class CapacitorCommand extends Command { ...conf, }; } - - private async promptToInstallCapacitor(): Promise { - if (!this.project) { - throw new FatalException(`Cannot use Capacitor outside a project directory.`); - } - - const { pkgManagerArgs } = await import('../../lib/utils/npm'); - - const pkg = '@capacitor/cli'; - const [ manager, ...managerArgs ] = await pkgManagerArgs(this.env.config.get('npmClient'), { pkg, command: 'install', saveDev: true }); - - const confirm = await this.env.prompt({ - name: 'confirm', - message: `Install ${input(pkg)}?`, - type: 'confirm', - }); - - if (!confirm) { - this.env.log.warn(`Not installing--here's how to install manually: ${input(`${manager} ${managerArgs.join(' ')}`)}`); - return false; - } - - await this.env.shell.run(manager, managerArgs, { cwd: this.integration.root }); - - return true; - } - - private async _runCapacitor(argList: string[]) { - if (!this.project) { - throw new FatalException(`Cannot use Capacitor outside a project directory.`); - } - - await this.env.shell.run('capacitor', argList, { fatalOnNotFound: false, truncateErrorOutput: 5000, stdio: 'inherit', cwd: this.integration.root }); - } } From 17e33004c3b3d2616f119bb07e19e93f24307b9c Mon Sep 17 00:00:00 2001 From: Daniel Imhoff Date: Thu, 19 Nov 2020 12:59:12 -0800 Subject: [PATCH 14/34] finalize preRun --- .../@ionic/cli/src/commands/capacitor/base.ts | 50 +++++- .../@ionic/cli/src/commands/capacitor/run.ts | 167 +++++++++--------- 2 files changed, 127 insertions(+), 90 deletions(-) diff --git a/packages/@ionic/cli/src/commands/capacitor/base.ts b/packages/@ionic/cli/src/commands/capacitor/base.ts index 61cf4d0b87..7e844a8dc6 100644 --- a/packages/@ionic/cli/src/commands/capacitor/base.ts +++ b/packages/@ionic/cli/src/commands/capacitor/base.ts @@ -1,4 +1,5 @@ import { pathExists } from '@ionic/utils-fs'; +import { onBeforeExit } from '@ionic/utils-process'; import * as lodash from 'lodash'; import * as path from 'path'; import * as semver from 'semver'; @@ -88,7 +89,7 @@ export abstract class CapacitorCommand extends Command { return this.project.createIntegration('capacitor'); }); - async getCapacitorVersion(): Promise { + getCapacitorVersion = lodash.memoize(async (): Promise => { const capacitor = await this.getCapacitorIntegration(); const version = semver.parse(await capacitor.getCapacitorCLIVersion()); @@ -97,7 +98,7 @@ export abstract class CapacitorCommand extends Command { } return version; - } + }); async checkCapacitor(runinfo: CommandInstanceInfo) { if (!this.project) { @@ -154,6 +155,51 @@ export abstract class CapacitorCommand extends Command { } } + async runServe(inputs: CommandLineInputs, options: CommandLineOptions): Promise { + if (!this.project) { + throw new FatalException(`Cannot run ${input('ionic capacitor run')} outside a project directory.`); + } + + const [ platform ] = inputs; + + try { + const runner = await this.project.requireServeRunner(); + const runnerOpts = runner.createOptionsFromCommandLine(inputs, generateOptionsForCapacitorBuild(inputs, options)); + + let serverUrl = options['livereload-url'] ? String(options['livereload-url']) : undefined; + + if (!serverUrl) { + const details = await runner.run(runnerOpts); + serverUrl = `${details.protocol || 'http'}://${details.externalAddress}:${details.port}`; + } + + const conf = await this.getGeneratedConfig(platform); + + onBeforeExit(async () => { + conf.resetServerUrl(); + }); + + conf.setServerUrl(serverUrl); + + const manifest = await this.getAndroidManifest(); + + if (platform === 'android') { + onBeforeExit(async () => { + await manifest.reset(); + }); + + manifest.enableCleartextTraffic(); + await manifest.save(); + } + } catch (e) { + if (e instanceof RunnerException) { + throw new FatalException(e.message); + } + + throw e; + } + } + async checkForPlatformInstallation(platform: string) { if (!this.project) { throw new FatalException('Cannot use Capacitor outside a project directory.'); diff --git a/packages/@ionic/cli/src/commands/capacitor/run.ts b/packages/@ionic/cli/src/commands/capacitor/run.ts index f9834c12c3..57d0249089 100644 --- a/packages/@ionic/cli/src/commands/capacitor/run.ts +++ b/packages/@ionic/cli/src/commands/capacitor/run.ts @@ -1,5 +1,5 @@ import { BaseError, Footnote, MetadataGroup, validators } from '@ionic/cli-framework'; -import { onBeforeExit, sleepForever } from '@ionic/utils-process'; +import { sleepForever } from '@ionic/utils-process'; import { columnar } from '@ionic/utils-terminal'; import * as chalk from 'chalk'; import * as Debug from 'debug'; @@ -9,15 +9,17 @@ import * as semver from 'semver'; import { COLUMNAR_OPTIONS } from '../../constants'; import { AnyBuildOptions, AnyServeOptions, CapacitorRunHookName, CommandInstanceInfo, CommandLineInputs, CommandLineOptions, CommandMetadata, CommandMetadataOption, CommandPreRun } from '../../definitions'; import { ancillary, input, strong, weak } from '../../lib/color'; -import { FatalException, RunnerException } from '../../lib/errors'; +import { FatalException } from '../../lib/errors'; import { Hook, HookDeps } from '../../lib/hooks'; -import { generateOptionsForCapacitorBuild, getNativeIDEForPlatform, getVirtualDeviceNameForPlatform } from '../../lib/integrations/capacitor/utils'; +import { getNativeIDEForPlatform, getVirtualDeviceNameForPlatform } from '../../lib/integrations/capacitor/utils'; import { COMMON_SERVE_COMMAND_OPTIONS } from '../../lib/serve'; import { CapacitorCommand } from './base'; const debug = Debug('ionic:commands:capacitor:run'); +const PLATFORMS = ['android', 'ios'] as const; + interface NativeTarget { id: string; name: string; @@ -131,7 +133,7 @@ For Android and iOS, you can setup Remote Debugging on your device with browser inputs: [ { name: 'platform', - summary: `The platform to run (e.g. ${['android', 'ios'].map(v => input(v)).join(', ')})`, + summary: `The platform to run (e.g. ${PLATFORMS.map(v => input(v)).join(', ')})`, validators: [validators.required], }, ], @@ -140,15 +142,32 @@ For Android and iOS, you can setup Remote Debugging on your device with browser }; } + isOldCapacitor = lodash.memoize(async (): Promise => { + const version = await this.getCapacitorVersion(); + return semver.lt(version, '3.0.0-alpha.7'); + }); + + async shouldRunCapacitorOpenFlow(inputs: CommandLineInputs, options: CommandLineOptions) { + return (await this.isOldCapacitor()) || options['open'] === true; + } + async preRun(inputs: CommandLineInputs, options: CommandLineOptions, runinfo: CommandInstanceInfo): Promise { await this.preRunChecks(runinfo); + if (await this.isOldCapacitor()) { + this.env.log.warn( + `Support for Capacitor 1 and 2 is deprecated.\n` + + `Please update to the latest Capacitor. Visit the docs${ancillary('[1]')} for upgrade guides.\n\n` + + `${ancillary('[1]')}: ${strong('https://capacitorjs.com/docs/')}\n` + ); + } + if (!inputs[0]) { const platform = await this.env.prompt({ type: 'list', name: 'platform', message: 'What platform would you like to run?', - choices: ['android', 'ios'], + choices: PLATFORMS, }); inputs[0] = platform.trim(); @@ -164,53 +183,62 @@ For Android and iOS, you can setup Remote Debugging on your device with browser } await this.checkForPlatformInstallation(inputs[0]); - } - - async run(inputs: CommandLineInputs, options: CommandLineOptions): Promise { - if (!this.project) { - throw new FatalException(`Cannot run ${input('ionic capacitor run')} outside a project directory.`); - } - const [ platform ] = inputs; + if (!(await this.shouldRunCapacitorOpenFlow(inputs, options))) { + const targets = inputs[0] ? await this.getNativeTargets(inputs[0]) : []; - const version = await this.getCapacitorVersion(); - const isOldCapacitor = semver.lt(version, '3.0.0-alpha.7'); - const doOpenFlow = isOldCapacitor || options['open'] === true; + if (options['list']) { + if (!inputs[0]) { + throw new FatalException(`The ${input('platform')} argument is required for the ${input('--list')} option.`); + } - if (isOldCapacitor) { - this.env.log.warn( - `Support for Capacitor 1 and 2 is deprecated.\n` + - `Please update to the latest Capacitor. Visit the docs${ancillary('[1]')} for upgrade guides.\n\n` + - `${ancillary('[1]')}: ${strong('https://capacitorjs.com/docs/')}\n` - ); - } + if (targets.length > 0) { + const rows = targets.map(t => [t.name, t.api, t.id]); + this.env.log.msg(columnar(rows, { ...COLUMNAR_OPTIONS, headers: ['Name', 'API', 'Target ID'] })); + } else { + this.env.log.info('No native targets found.'); + } - const targets = doOpenFlow ? [] : await this.getNativeTargets(platform); + throw new FatalException('', 0); + } - if (options['list']) { - const rows = targets.map(t => [t.name, t.api, t.id]); - this.env.log.msg(columnar(rows, { ...COLUMNAR_OPTIONS, headers: ['Name', 'API', 'Target ID'] })); + const target = options['target']; - throw new FatalException('', 0); + if (typeof target === 'string') { + if (!targets.map(t => t.id).find(t => t === target)) { + throw new FatalException( + `${input(target)} is not a valid Target ID.\n` + + `Use the ${input('--list')} option to list all targets.\n` + ); + } + } else { + options['target'] = await this.env.prompt({ + type: 'list', + name: 'target', + message: 'Which device would you like to target?', + choices: targets.map(t => ({ name: `${t.name} (${t.id})`, value: t.id })), + }); + + // TODO: check for target missing in non-interactive + } } + } - await this.runCapacitor(['sync', platform]); + async run(inputs: CommandLineInputs, options: CommandLineOptions): Promise { + if (!this.project) { + throw new FatalException(`Cannot run ${input('ionic capacitor run')} outside a project directory.`); + } - try { - if (options['livereload']) { - await this.runServe(inputs, options); - } else { - await this.runBuild(inputs, options); - } - } catch (e) { - if (e instanceof RunnerException) { - throw new FatalException(e.message); - } + const [ platform ] = inputs; - throw e; + if (options['livereload']) { + await this.runServe(inputs, options); + } else { + await this.runBuild(inputs, options); } - if (doOpenFlow) { + if (await this.shouldRunCapacitorOpenFlow(inputs, options)) { + await this.runCapacitor(['sync', platform]); await this.runCapacitorOpenFlow(inputs, options); } else { await this.runCapacitorRunFlow(inputs, options); @@ -227,42 +255,23 @@ For Android and iOS, you can setup Remote Debugging on your device with browser } } - async runServe(inputs: CommandLineInputs, options: CommandLineOptions): Promise { - if (!this.project) { - throw new FatalException(`Cannot run ${input('ionic capacitor run')} outside a project directory.`); - } - - const [ platform ] = inputs; + protected getNativeTargets = lodash.memoize(async (platform: string): Promise => { + const args = ['run', platform, '--list', '--json']; - const runner = await this.project.requireServeRunner(); - const runnerOpts = runner.createOptionsFromCommandLine(inputs, generateOptionsForCapacitorBuild(inputs, options)); + debug('Getting native targets for %O with Capacitor CLI: %O', platform, args); - let serverUrl = options['livereload-url'] ? String(options['livereload-url']) : undefined; + const output = await this.env.shell.cmdinfo('capacitor', args, { cwd: this.integration.root }); - if (!serverUrl) { - const details = await runner.run(runnerOpts); - serverUrl = `${details.protocol || 'http'}://${details.externalAddress}:${details.port}`; + if (!output) { + return []; } - const conf = await this.getGeneratedConfig(platform); - - onBeforeExit(async () => { - conf.resetServerUrl(); - }); - - conf.setServerUrl(serverUrl); - - const manifest = await this.getAndroidManifest(); + const targets = JSON.parse(output); - if (platform === 'android') { - onBeforeExit(async () => { - await manifest.reset(); - }); + debug('%O native targets found', targets.length); - manifest.enableCleartextTraffic(); - await manifest.save(); - } - } + return targets; + }); protected async runCapacitorOpenFlow(inputs: CommandLineInputs, options: CommandLineOptions): Promise { if (!this.project) { @@ -301,25 +310,7 @@ For Android and iOS, you can setup Remote Debugging on your device with browser const [ platform ] = inputs; await this.runCapacitorRunHook('capacitor:run:before', inputs, options, { ...this.env, project: this.project }); - await this.runCapacitor(['run', platform, '--no-sync', ...(typeof options['target'] === 'string' ? ['--target', options['target']] : [])]); - } - - protected async getNativeTargets(platform: string): Promise { - const args = ['run', platform, '--list', '--json']; - - debug('Getting native targets for %O with Capacitor CLI: %O', platform, args); - - const output = await this.env.shell.cmdinfo('capacitor', args, { cwd: this.integration.root }); - - if (!output) { - return []; - } - - const targets = JSON.parse(output); - - debug('%O native targets found', targets.length); - - return targets; + await this.runCapacitor(['run', platform, '--target', String(options['target'])]); } protected async runCapacitorRunHook(name: CapacitorRunHookName, inputs: CommandLineInputs, options: CommandLineOptions, e: HookDeps): Promise { From 62f186952e9dc6fd3e679b670df184285161d5e2 Mon Sep 17 00:00:00 2001 From: Daniel Imhoff Date: Thu, 19 Nov 2020 13:30:28 -0800 Subject: [PATCH 15/34] do the thing --- packages/@ionic/cli/src/commands/capacitor/run.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/@ionic/cli/src/commands/capacitor/run.ts b/packages/@ionic/cli/src/commands/capacitor/run.ts index 57d0249089..2de3613b0f 100644 --- a/packages/@ionic/cli/src/commands/capacitor/run.ts +++ b/packages/@ionic/cli/src/commands/capacitor/run.ts @@ -219,7 +219,13 @@ For Android and iOS, you can setup Remote Debugging on your device with browser choices: targets.map(t => ({ name: `${t.name} (${t.id})`, value: t.id })), }); - // TODO: check for target missing in non-interactive + if (!inputs[0]) { + throw new FatalException(`The ${input('platform')} argument is required.`); + } + + if (!options['target']) { + throw new FatalException(`The ${input('--target')} option is required.`); + } } } } From 792da1de271df62bc10f609191f55da4d2ba447c Mon Sep 17 00:00:00 2001 From: Daniel Imhoff Date: Thu, 19 Nov 2020 13:31:33 -0800 Subject: [PATCH 16/34] words --- packages/@ionic/cli/src/commands/capacitor/run.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@ionic/cli/src/commands/capacitor/run.ts b/packages/@ionic/cli/src/commands/capacitor/run.ts index 2de3613b0f..bff45373aa 100644 --- a/packages/@ionic/cli/src/commands/capacitor/run.ts +++ b/packages/@ionic/cli/src/commands/capacitor/run.ts @@ -166,7 +166,7 @@ For Android and iOS, you can setup Remote Debugging on your device with browser const platform = await this.env.prompt({ type: 'list', name: 'platform', - message: 'What platform would you like to run?', + message: options['list'] ? 'What platform targets would you like to list?' : 'What platform would you like to run?', choices: PLATFORMS, }); From 0433fb4c47abd35c08bec6999e839d8a0cc26371 Mon Sep 17 00:00:00 2001 From: Daniel Imhoff Date: Thu, 19 Nov 2020 13:43:18 -0800 Subject: [PATCH 17/34] add checks for options that don't work with old capacitor --- packages/@ionic/cli/src/commands/capacitor/run.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/packages/@ionic/cli/src/commands/capacitor/run.ts b/packages/@ionic/cli/src/commands/capacitor/run.ts index bff45373aa..1f836f9068 100644 --- a/packages/@ionic/cli/src/commands/capacitor/run.ts +++ b/packages/@ionic/cli/src/commands/capacitor/run.ts @@ -160,6 +160,14 @@ For Android and iOS, you can setup Remote Debugging on your device with browser `Please update to the latest Capacitor. Visit the docs${ancillary('[1]')} for upgrade guides.\n\n` + `${ancillary('[1]')}: ${strong('https://capacitorjs.com/docs/')}\n` ); + + if (options['list']) { + throw new FatalException(`The ${input('--list')} option is not supported with your Capacitor version.`); + } + + if (options['target']) { + throw new FatalException(`The ${input('--target')} option is not supported with your Capacitor version.`); + } } if (!inputs[0]) { From 762e0e9fab0c693e49c16e09b5896ba145cc50d1 Mon Sep 17 00:00:00 2001 From: Daniel Imhoff Date: Thu, 19 Nov 2020 13:47:57 -0800 Subject: [PATCH 18/34] capacitor prefix --- packages/@ionic/cli/src/commands/capacitor/base.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/@ionic/cli/src/commands/capacitor/base.ts b/packages/@ionic/cli/src/commands/capacitor/base.ts index 7e844a8dc6..f5a704847c 100644 --- a/packages/@ionic/cli/src/commands/capacitor/base.ts +++ b/packages/@ionic/cli/src/commands/capacitor/base.ts @@ -5,7 +5,7 @@ import * as path from 'path'; import * as semver from 'semver'; import { CommandInstanceInfo, CommandLineInputs, CommandLineOptions, IonicCapacitorOptions, ProjectIntegration } from '../../definitions'; -import { input } from '../../lib/color'; +import { input, weak } from '../../lib/color'; import { Command } from '../../lib/command'; import { FatalException, RunnerException } from '../../lib/errors'; import { runCommand } from '../../lib/executor'; @@ -13,6 +13,7 @@ import type { CapacitorCLIConfig, Integration as CapacitorIntegration } from '.. import { ANDROID_MANIFEST_FILE, CapacitorAndroidManifest } from '../../lib/integrations/capacitor/android'; import { CAPACITOR_CONFIG_JSON_FILE, CapacitorJSONConfig } from '../../lib/integrations/capacitor/config'; import { generateOptionsForCapacitorBuild } from '../../lib/integrations/capacitor/utils'; +import { createPrefixedWriteStream } from '../../lib/utils/logger'; export abstract class CapacitorCommand extends Command { private _integration?: Required; @@ -121,7 +122,9 @@ export abstract class CapacitorCommand extends Command { throw new FatalException(`Cannot use Capacitor outside a project directory.`); } - await this.env.shell.run('capacitor', argList, { fatalOnNotFound: false, truncateErrorOutput: 5000, stdio: 'inherit', cwd: this.integration.root }); + const stream = createPrefixedWriteStream(this.env.log, weak(`[capacitor]`)); + + await this.env.shell.run('capacitor', argList, { stream, fatalOnNotFound: false, truncateErrorOutput: 5000, cwd: this.integration.root }); } async runBuild(inputs: CommandLineInputs, options: CommandLineOptions): Promise { From 09921dc83877d251a2e23f104687a5512e9b1bda Mon Sep 17 00:00:00 2001 From: Daniel Imhoff Date: Thu, 19 Nov 2020 14:05:30 -0800 Subject: [PATCH 19/34] fix --open and --list usage --- .../@ionic/cli/src/commands/capacitor/run.ts | 54 +++++++++---------- 1 file changed, 26 insertions(+), 28 deletions(-) diff --git a/packages/@ionic/cli/src/commands/capacitor/run.ts b/packages/@ionic/cli/src/commands/capacitor/run.ts index 1f836f9068..72da7dd069 100644 --- a/packages/@ionic/cli/src/commands/capacitor/run.ts +++ b/packages/@ionic/cli/src/commands/capacitor/run.ts @@ -147,10 +147,6 @@ For Android and iOS, you can setup Remote Debugging on your device with browser return semver.lt(version, '3.0.0-alpha.7'); }); - async shouldRunCapacitorOpenFlow(inputs: CommandLineInputs, options: CommandLineOptions) { - return (await this.isOldCapacitor()) || options['open'] === true; - } - async preRun(inputs: CommandLineInputs, options: CommandLineOptions, runinfo: CommandInstanceInfo): Promise { await this.preRunChecks(runinfo); @@ -192,7 +188,7 @@ For Android and iOS, you can setup Remote Debugging on your device with browser await this.checkForPlatformInstallation(inputs[0]); - if (!(await this.shouldRunCapacitorOpenFlow(inputs, options))) { + if (!(await this.isOldCapacitor())) { const targets = inputs[0] ? await this.getNativeTargets(inputs[0]) : []; if (options['list']) { @@ -210,29 +206,31 @@ For Android and iOS, you can setup Remote Debugging on your device with browser throw new FatalException('', 0); } - const target = options['target']; - - if (typeof target === 'string') { - if (!targets.map(t => t.id).find(t => t === target)) { - throw new FatalException( - `${input(target)} is not a valid Target ID.\n` + - `Use the ${input('--list')} option to list all targets.\n` - ); - } - } else { - options['target'] = await this.env.prompt({ - type: 'list', - name: 'target', - message: 'Which device would you like to target?', - choices: targets.map(t => ({ name: `${t.name} (${t.id})`, value: t.id })), - }); - - if (!inputs[0]) { - throw new FatalException(`The ${input('platform')} argument is required.`); - } + if (!options['open']) { + const target = options['target']; - if (!options['target']) { - throw new FatalException(`The ${input('--target')} option is required.`); + if (typeof target === 'string') { + if (!targets.map(t => t.id).find(t => t === target)) { + throw new FatalException( + `${input(target)} is not a valid Target ID.\n` + + `Use the ${input('--list')} option to list all targets.\n` + ); + } + } else { + options['target'] = await this.env.prompt({ + type: 'list', + name: 'target', + message: 'Which device would you like to target?', + choices: targets.map(t => ({ name: `${t.name} (${t.id})`, value: t.id })), + }); + + if (!inputs[0]) { + throw new FatalException(`The ${input('platform')} argument is required.`); + } + + if (!options['target']) { + throw new FatalException(`The ${input('--target')} option is required.`); + } } } } @@ -251,7 +249,7 @@ For Android and iOS, you can setup Remote Debugging on your device with browser await this.runBuild(inputs, options); } - if (await this.shouldRunCapacitorOpenFlow(inputs, options)) { + if ((await this.isOldCapacitor()) || options['open'] === true) { await this.runCapacitor(['sync', platform]); await this.runCapacitorOpenFlow(inputs, options); } else { From 02a78c5ebf53567b4620a993879affc8fa12c3da Mon Sep 17 00:00:00 2001 From: Daniel Imhoff Date: Sat, 21 Nov 2020 12:47:03 -0800 Subject: [PATCH 20/34] refactor to clear up the flows --- .../@ionic/cli/src/commands/capacitor/run.ts | 38 +++++++++++-------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/packages/@ionic/cli/src/commands/capacitor/run.ts b/packages/@ionic/cli/src/commands/capacitor/run.ts index 72da7dd069..1e702730f7 100644 --- a/packages/@ionic/cli/src/commands/capacitor/run.ts +++ b/packages/@ionic/cli/src/commands/capacitor/run.ts @@ -243,20 +243,19 @@ For Android and iOS, you can setup Remote Debugging on your device with browser const [ platform ] = inputs; - if (options['livereload']) { - await this.runServe(inputs, options); - } else { - await this.runBuild(inputs, options); - } - - if ((await this.isOldCapacitor()) || options['open'] === true) { - await this.runCapacitor(['sync', platform]); - await this.runCapacitorOpenFlow(inputs, options); - } else { - await this.runCapacitorRunFlow(inputs, options); - } + const doLiveReload = !!options['livereload']; + const doOpenFlow = (await this.isOldCapacitor()) || options['open'] === true; + + if (doLiveReload) { + if (doOpenFlow) { + await this.runServe(inputs, options); + await this.runCapacitorOpenFlow(inputs, options); + } else { + await this.runCapacitor(['sync', platform]); + await this.runServe(inputs, options); + await this.runCapacitorRunFlow(inputs, options, { shouldSync: false }); + } - if (options['livereload']) { this.env.log.nl(); this.env.log.info( 'Development server will continue running until manually stopped.\n' + @@ -264,6 +263,15 @@ For Android and iOS, you can setup Remote Debugging on your device with browser ); await sleepForever(); + } else { + if (doOpenFlow) { + await this.runBuild(inputs, options); + await this.runCapacitor(['sync', platform]); + await this.runCapacitorOpenFlow(inputs, options); + } else { + await this.runBuild(inputs, options); + await this.runCapacitorRunFlow(inputs, options, { shouldSync: true }); + } } } @@ -314,7 +322,7 @@ For Android and iOS, you can setup Remote Debugging on your device with browser ); } - protected async runCapacitorRunFlow(inputs: CommandLineInputs, options: CommandLineOptions): Promise { + protected async runCapacitorRunFlow(inputs: CommandLineInputs, options: CommandLineOptions, { shouldSync = true }: { shouldSync?: boolean } = {}): Promise { if (!this.project) { throw new FatalException(`Cannot run ${input('ionic capacitor run')} outside a project directory.`); } @@ -322,7 +330,7 @@ For Android and iOS, you can setup Remote Debugging on your device with browser const [ platform ] = inputs; await this.runCapacitorRunHook('capacitor:run:before', inputs, options, { ...this.env, project: this.project }); - await this.runCapacitor(['run', platform, '--target', String(options['target'])]); + await this.runCapacitor(['run', platform, ...(shouldSync ? [] : ['--no-sync']), '--target', String(options['target'])]); } protected async runCapacitorRunHook(name: CapacitorRunHookName, inputs: CommandLineInputs, options: CommandLineOptions, e: HookDeps): Promise { From 4a307982d06b6bfa1f9188767d559ab292a14519 Mon Sep 17 00:00:00 2001 From: Daniel Imhoff Date: Sat, 21 Nov 2020 12:48:54 -0800 Subject: [PATCH 21/34] only print for open flow --- packages/@ionic/cli/src/commands/capacitor/run.ts | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/packages/@ionic/cli/src/commands/capacitor/run.ts b/packages/@ionic/cli/src/commands/capacitor/run.ts index 1e702730f7..b3b760e63c 100644 --- a/packages/@ionic/cli/src/commands/capacitor/run.ts +++ b/packages/@ionic/cli/src/commands/capacitor/run.ts @@ -250,18 +250,20 @@ For Android and iOS, you can setup Remote Debugging on your device with browser if (doOpenFlow) { await this.runServe(inputs, options); await this.runCapacitorOpenFlow(inputs, options); + + // output can get messy with the open flow, so print this type of + // message again + this.env.log.nl(); + this.env.log.info( + 'Development server will continue running until manually stopped.\n' + + chalk.yellow('Use Ctrl+C to quit this process') + ); } else { await this.runCapacitor(['sync', platform]); await this.runServe(inputs, options); await this.runCapacitorRunFlow(inputs, options, { shouldSync: false }); } - this.env.log.nl(); - this.env.log.info( - 'Development server will continue running until manually stopped.\n' + - chalk.yellow('Use Ctrl+C to quit this process') - ); - await sleepForever(); } else { if (doOpenFlow) { From 042c37f00239b53fb1b41714b4942b7f19512cea Mon Sep 17 00:00:00 2001 From: Daniel Imhoff Date: Sat, 21 Nov 2020 12:56:35 -0800 Subject: [PATCH 22/34] message for run as well --- packages/@ionic/cli/src/commands/capacitor/run.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/@ionic/cli/src/commands/capacitor/run.ts b/packages/@ionic/cli/src/commands/capacitor/run.ts index b3b760e63c..0ec9c49257 100644 --- a/packages/@ionic/cli/src/commands/capacitor/run.ts +++ b/packages/@ionic/cli/src/commands/capacitor/run.ts @@ -251,8 +251,6 @@ For Android and iOS, you can setup Remote Debugging on your device with browser await this.runServe(inputs, options); await this.runCapacitorOpenFlow(inputs, options); - // output can get messy with the open flow, so print this type of - // message again this.env.log.nl(); this.env.log.info( 'Development server will continue running until manually stopped.\n' + @@ -262,6 +260,13 @@ For Android and iOS, you can setup Remote Debugging on your device with browser await this.runCapacitor(['sync', platform]); await this.runServe(inputs, options); await this.runCapacitorRunFlow(inputs, options, { shouldSync: false }); + + this.env.log.nl(); + this.env.log.info( + `App deployed to device!\n` + + 'Development server will continue running until manually stopped.\n\n' + + chalk.yellow('Use Ctrl+C to quit this process') + ); } await sleepForever(); From 08387fa8ef360e891bfc03454370410cb875faf2 Mon Sep 17 00:00:00 2001 From: Daniel Imhoff Date: Sun, 29 Nov 2020 11:29:07 -0800 Subject: [PATCH 23/34] better error management when getting version --- .../@ionic/cli/src/commands/capacitor/base.ts | 25 ++++++++++++++----- .../@ionic/cli/src/commands/capacitor/run.ts | 10 ++++---- packages/@ionic/cli/src/lib/shell.ts | 6 +---- 3 files changed, 25 insertions(+), 16 deletions(-) diff --git a/packages/@ionic/cli/src/commands/capacitor/base.ts b/packages/@ionic/cli/src/commands/capacitor/base.ts index f5a704847c..c9de96b2ef 100644 --- a/packages/@ionic/cli/src/commands/capacitor/base.ts +++ b/packages/@ionic/cli/src/commands/capacitor/base.ts @@ -1,5 +1,6 @@ import { pathExists } from '@ionic/utils-fs'; import { onBeforeExit } from '@ionic/utils-process'; +import { ERROR_COMMAND_NOT_FOUND, SubprocessError } from '@ionic/utils-subprocess'; import * as lodash from 'lodash'; import * as path from 'path'; import * as semver from 'semver'; @@ -91,14 +92,26 @@ export abstract class CapacitorCommand extends Command { }); getCapacitorVersion = lodash.memoize(async (): Promise => { - const capacitor = await this.getCapacitorIntegration(); - const version = semver.parse(await capacitor.getCapacitorCLIVersion()); + try { + const proc = await this.env.shell.createSubprocess('capacitor', ['--version'], { cwd: this.integration.root }); + const version = semver.parse((await proc.output()).trim()); - if (!version) { - throw new FatalException('Error while getting Capacitor CLI version. Is Capacitor installed?'); - } + if (!version) { + throw new FatalException('Error while parsing Capacitor CLI version.'); + } - return version; + return version; + } catch (e) { + if (e instanceof SubprocessError) { + if (e.code === ERROR_COMMAND_NOT_FOUND) { + throw new FatalException('Error while getting Capacitor CLI version. Is Capacitor installed?'); + } + + throw new FatalException('Error while getting Capacitor CLI version.\n' + (e.output ?? e.code)); + } + + throw e; + } }); async checkCapacitor(runinfo: CommandInstanceInfo) { diff --git a/packages/@ionic/cli/src/commands/capacitor/run.ts b/packages/@ionic/cli/src/commands/capacitor/run.ts index 0ec9c49257..d2246e1baf 100644 --- a/packages/@ionic/cli/src/commands/capacitor/run.ts +++ b/packages/@ionic/cli/src/commands/capacitor/run.ts @@ -247,8 +247,10 @@ For Android and iOS, you can setup Remote Debugging on your device with browser const doOpenFlow = (await this.isOldCapacitor()) || options['open'] === true; if (doLiveReload) { + await this.runCapacitor(['sync', platform]); + await this.runServe(inputs, options); + if (doOpenFlow) { - await this.runServe(inputs, options); await this.runCapacitorOpenFlow(inputs, options); this.env.log.nl(); @@ -257,8 +259,6 @@ For Android and iOS, you can setup Remote Debugging on your device with browser chalk.yellow('Use Ctrl+C to quit this process') ); } else { - await this.runCapacitor(['sync', platform]); - await this.runServe(inputs, options); await this.runCapacitorRunFlow(inputs, options, { shouldSync: false }); this.env.log.nl(); @@ -271,12 +271,12 @@ For Android and iOS, you can setup Remote Debugging on your device with browser await sleepForever(); } else { + await this.runBuild(inputs, options); + if (doOpenFlow) { - await this.runBuild(inputs, options); await this.runCapacitor(['sync', platform]); await this.runCapacitorOpenFlow(inputs, options); } else { - await this.runBuild(inputs, options); await this.runCapacitorRunFlow(inputs, options, { shouldSync: true }); } } diff --git a/packages/@ionic/cli/src/lib/shell.ts b/packages/@ionic/cli/src/lib/shell.ts index 3b237e91f1..a634948665 100644 --- a/packages/@ionic/cli/src/lib/shell.ts +++ b/packages/@ionic/cli/src/lib/shell.ts @@ -33,8 +33,6 @@ export class Shell implements IShell { } async run(command: string, args: readonly string[], { stream, killOnExit = true, showCommand = true, showError = true, fatalOnNotFound = true, fatalOnError = true, truncateErrorOutput, ...crossSpawnOptions }: IShellRunOptions): Promise { - this.prepareSpawnOptions(crossSpawnOptions); - const proc = await this.createSubprocess(command, args, crossSpawnOptions); const fullCmd = proc.bashify(); @@ -186,8 +184,6 @@ export class Shell implements IShell { } async spawn(command: string, args: readonly string[], { showCommand = true, ...crossSpawnOptions }: IShellSpawnOptions): Promise { - this.prepareSpawnOptions(crossSpawnOptions); - const proc = await this.createSubprocess(command, args, crossSpawnOptions); const p = proc.spawn(); @@ -200,7 +196,6 @@ export class Shell implements IShell { async cmdinfo(command: string, args: readonly string[] = []): Promise { const opts: IShellSpawnOptions = {}; - this.prepareSpawnOptions(opts); const proc = await this.createSubprocess(command, args, opts); @@ -213,6 +208,7 @@ export class Shell implements IShell { } async createSubprocess(command: string, args: readonly string[] = [], options: SubprocessOptions = {}): Promise { + this.prepareSpawnOptions(options); const cmdpath = await this.resolveCommandPath(command, options); const proc = new Subprocess(cmdpath, args, options); From 2a5ac8054b69b918e7cb884574c5456751120365 Mon Sep 17 00:00:00 2001 From: Daniel Imhoff Date: Tue, 8 Dec 2020 22:25:15 -0800 Subject: [PATCH 24/34] install package for Cap 3 --- .../@ionic/cli/src/commands/capacitor/add.ts | 20 ++++++++++++++---- .../@ionic/cli/src/commands/capacitor/base.ts | 21 ++++++++++++++++++- 2 files changed, 36 insertions(+), 5 deletions(-) diff --git a/packages/@ionic/cli/src/commands/capacitor/add.ts b/packages/@ionic/cli/src/commands/capacitor/add.ts index cda6a417cf..ca07072c71 100644 --- a/packages/@ionic/cli/src/commands/capacitor/add.ts +++ b/packages/@ionic/cli/src/commands/capacitor/add.ts @@ -1,7 +1,10 @@ import { validators } from '@ionic/cli-framework'; +import * as semver from 'semver'; import { CommandInstanceInfo, CommandLineInputs, CommandLineOptions, CommandMetadata, CommandPreRun } from '../../definitions'; import { input } from '../../lib/color'; +import { FatalException } from '../../lib/errors'; +import { pkgManagerArgs } from '../../lib/utils/npm'; import { CapacitorCommand } from './base'; @@ -13,7 +16,8 @@ export class AddCommand extends CapacitorCommand implements CommandPreRun { summary: 'Add a native platform to your Ionic project', description: ` ${input('ionic capacitor add')} will do the following: -- Add a new platform specific folder to your project (ios, android, or electron) +- Install the Capacitor platform package +- Copy the native platform template into your project `, inputs: [ { @@ -42,12 +46,20 @@ ${input('ionic capacitor add')} will do the following: async run(inputs: CommandLineInputs, options: CommandLineOptions): Promise { const [ platform ] = inputs; - const args = ['add']; + const version = await this.getCapacitorVersion(); - if (platform) { - args.push(platform); + const installedPlatforms = await this.getInstalledPlatforms(); + + if (installedPlatforms.includes(platform)) { + throw new FatalException(`The ${input(platform)} platform is already installed!`); + } + + if (semver.gte(version, '3.0.0-alpha.1')) { + const [ manager, ...managerArgs ] = await pkgManagerArgs(this.env.config.get('npmClient'), { command: 'install', pkg: `@capacitor/${platform}`, saveDev: true }); + await this.env.shell.run(manager, managerArgs, { cwd: this.integration.root }); } + const args = ['add', platform]; await this.runCapacitor(args); } } diff --git a/packages/@ionic/cli/src/commands/capacitor/base.ts b/packages/@ionic/cli/src/commands/capacitor/base.ts index c9de96b2ef..c98d43eef8 100644 --- a/packages/@ionic/cli/src/commands/capacitor/base.ts +++ b/packages/@ionic/cli/src/commands/capacitor/base.ts @@ -107,13 +107,32 @@ export abstract class CapacitorCommand extends Command { throw new FatalException('Error while getting Capacitor CLI version. Is Capacitor installed?'); } - throw new FatalException('Error while getting Capacitor CLI version.\n' + (e.output ?? e.code)); + throw new FatalException('Error while getting Capacitor CLI version.\n' + (e.output ? e.output : e.code)); } throw e; } }); + async getInstalledPlatforms(): Promise { + const cli = await this.getCapacitorCLIConfig(); + const platforms: string[] = []; + + if (!cli) { + return []; + } + + if (await pathExists(cli.android.platformDirAbs)) { + platforms.push('android'); + } + + if (await pathExists(cli.ios.platformDirAbs)) { + platforms.push('ios'); + } + + return platforms; + } + async checkCapacitor(runinfo: CommandInstanceInfo) { if (!this.project) { throw new FatalException(`Cannot use Capacitor outside a project directory.`); From 1f20e03832bcb320c64e3ec29285cdc1ad058182 Mon Sep 17 00:00:00 2001 From: Daniel Imhoff Date: Tue, 8 Dec 2020 22:42:32 -0800 Subject: [PATCH 25/34] rm electron --- .../@ionic/cli/src/commands/capacitor/add.ts | 22 ++--------- .../@ionic/cli/src/commands/capacitor/base.ts | 37 +++++++++++++++---- .../@ionic/cli/src/commands/capacitor/copy.ts | 2 +- .../@ionic/cli/src/commands/capacitor/sync.ts | 2 +- .../cli/src/commands/capacitor/update.ts | 2 +- 5 files changed, 35 insertions(+), 30 deletions(-) diff --git a/packages/@ionic/cli/src/commands/capacitor/add.ts b/packages/@ionic/cli/src/commands/capacitor/add.ts index ca07072c71..8aac8f47b5 100644 --- a/packages/@ionic/cli/src/commands/capacitor/add.ts +++ b/packages/@ionic/cli/src/commands/capacitor/add.ts @@ -1,10 +1,7 @@ import { validators } from '@ionic/cli-framework'; -import * as semver from 'semver'; import { CommandInstanceInfo, CommandLineInputs, CommandLineOptions, CommandMetadata, CommandPreRun } from '../../definitions'; import { input } from '../../lib/color'; -import { FatalException } from '../../lib/errors'; -import { pkgManagerArgs } from '../../lib/utils/npm'; import { CapacitorCommand } from './base'; @@ -22,7 +19,7 @@ ${input('ionic capacitor add')} will do the following: inputs: [ { name: 'platform', - summary: `The platform to add (e.g. ${['android', 'ios', 'electron'].map(v => input(v)).join(', ')})`, + summary: `The platform to add (e.g. ${['android', 'ios'].map(v => input(v)).join(', ')})`, validators: [validators.required], }, ], @@ -37,7 +34,7 @@ ${input('ionic capacitor add')} will do the following: type: 'list', name: 'platform', message: 'What platform would you like to add?', - choices: ['android', 'ios', 'electron'], + choices: ['android', 'ios'], }); inputs[0] = platform.trim(); @@ -46,20 +43,7 @@ ${input('ionic capacitor add')} will do the following: async run(inputs: CommandLineInputs, options: CommandLineOptions): Promise { const [ platform ] = inputs; - const version = await this.getCapacitorVersion(); - const installedPlatforms = await this.getInstalledPlatforms(); - - if (installedPlatforms.includes(platform)) { - throw new FatalException(`The ${input(platform)} platform is already installed!`); - } - - if (semver.gte(version, '3.0.0-alpha.1')) { - const [ manager, ...managerArgs ] = await pkgManagerArgs(this.env.config.get('npmClient'), { command: 'install', pkg: `@capacitor/${platform}`, saveDev: true }); - await this.env.shell.run(manager, managerArgs, { cwd: this.integration.root }); - } - - const args = ['add', platform]; - await this.runCapacitor(args); + await this.installPlatform(platform); } } diff --git a/packages/@ionic/cli/src/commands/capacitor/base.ts b/packages/@ionic/cli/src/commands/capacitor/base.ts index c98d43eef8..d4845c5d7a 100644 --- a/packages/@ionic/cli/src/commands/capacitor/base.ts +++ b/packages/@ionic/cli/src/commands/capacitor/base.ts @@ -15,6 +15,7 @@ import { ANDROID_MANIFEST_FILE, CapacitorAndroidManifest } from '../../lib/integ import { CAPACITOR_CONFIG_JSON_FILE, CapacitorJSONConfig } from '../../lib/integrations/capacitor/config'; import { generateOptionsForCapacitorBuild } from '../../lib/integrations/capacitor/utils'; import { createPrefixedWriteStream } from '../../lib/utils/logger'; +import { pkgManagerArgs } from '../../lib/utils/npm'; export abstract class CapacitorCommand extends Command { private _integration?: Required; @@ -235,6 +236,16 @@ export abstract class CapacitorCommand extends Command { } } + async isPlatformInstalled(platform: string): Promise { + const cli = await this.getCapacitorCLIConfig(); + + if (!cli || (platform !== 'android' && platform !== 'ios')) { + return false; + } + + return await pathExists(cli[platform].platformDirAbs); + } + async checkForPlatformInstallation(platform: string) { if (!this.project) { throw new FatalException('Cannot use Capacitor outside a project directory.'); @@ -247,18 +258,28 @@ export abstract class CapacitorCommand extends Command { throw new FatalException('Cannot check platform installations--Capacitor not yet integrated.'); } - const integrationRoot = capacitor.root; - const platformsToCheck = ['android', 'ios', 'electron']; - const platforms = (await Promise.all(platformsToCheck.map(async (p): Promise<[string, boolean]> => [p, await pathExists(path.resolve(integrationRoot, p))]))) - .filter(([, e]) => e) - .map(([p]) => p); - - if (!platforms.includes(platform)) { - await this.runCapacitor(['add', platform]); + if (!(await this.isPlatformInstalled(platform))) { + await this.installPlatform(platform); } } } + async installPlatform(platform: string): Promise { + const version = await this.getCapacitorVersion(); + const installedPlatforms = await this.getInstalledPlatforms(); + + if (installedPlatforms.includes(platform)) { + throw new FatalException(`The ${input(platform)} platform is already installed!`); + } + + if (semver.gte(version, '3.0.0-alpha.1')) { + const [ manager, ...managerArgs ] = await pkgManagerArgs(this.env.config.get('npmClient'), { command: 'install', pkg: `@capacitor/${platform}`, saveDev: true }); + await this.env.shell.run(manager, managerArgs, { cwd: this.integration.root }); + } + + await this.runCapacitor(['add', platform]); + } + protected async createOptionsFromCommandLine(inputs: CommandLineInputs, options: CommandLineOptions): Promise { const separatedArgs = options['--']; const verbose = !!options['verbose']; diff --git a/packages/@ionic/cli/src/commands/capacitor/copy.ts b/packages/@ionic/cli/src/commands/capacitor/copy.ts index d364d374c1..b6440bba32 100644 --- a/packages/@ionic/cli/src/commands/capacitor/copy.ts +++ b/packages/@ionic/cli/src/commands/capacitor/copy.ts @@ -35,7 +35,7 @@ ${input('ionic capacitor copy')} will do the following: inputs: [ { name: 'platform', - summary: `The platform to copy (e.g. ${['android', 'ios', 'electron'].map(v => input(v)).join(', ')})`, + summary: `The platform to copy (e.g. ${['android', 'ios'].map(v => input(v)).join(', ')})`, }, ], options, diff --git a/packages/@ionic/cli/src/commands/capacitor/sync.ts b/packages/@ionic/cli/src/commands/capacitor/sync.ts index a0a8fbb265..166956044b 100644 --- a/packages/@ionic/cli/src/commands/capacitor/sync.ts +++ b/packages/@ionic/cli/src/commands/capacitor/sync.ts @@ -39,7 +39,7 @@ ${input('ionic capacitor sync')} will do the following: inputs: [ { name: 'platform', - summary: `The platform to sync (e.g. ${['android', 'ios', 'electron'].map(v => input(v)).join(', ')})`, + summary: `The platform to sync (e.g. ${['android', 'ios'].map(v => input(v)).join(', ')})`, }, ], options, diff --git a/packages/@ionic/cli/src/commands/capacitor/update.ts b/packages/@ionic/cli/src/commands/capacitor/update.ts index ec34ee2ff3..6895a9a4ee 100644 --- a/packages/@ionic/cli/src/commands/capacitor/update.ts +++ b/packages/@ionic/cli/src/commands/capacitor/update.ts @@ -17,7 +17,7 @@ ${input('ionic capacitor update')} will do the following: inputs: [ { name: 'platform', - summary: `The platform to update (e.g. ${['android', 'ios', 'electron'].map(v => input(v)).join(', ')})`, + summary: `The platform to update (e.g. ${['android', 'ios'].map(v => input(v)).join(', ')})`, }, ], }; From 7d839960104240b73256694c8d6420c53160b5b5 Mon Sep 17 00:00:00 2001 From: Daniel Imhoff Date: Mon, 28 Dec 2020 12:07:33 -0800 Subject: [PATCH 26/34] fix installed platforms for v2 --- .../@ionic/cli/src/commands/capacitor/base.ts | 26 +++++++------------ 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/packages/@ionic/cli/src/commands/capacitor/base.ts b/packages/@ionic/cli/src/commands/capacitor/base.ts index d4845c5d7a..861f6138f6 100644 --- a/packages/@ionic/cli/src/commands/capacitor/base.ts +++ b/packages/@ionic/cli/src/commands/capacitor/base.ts @@ -117,23 +117,27 @@ export abstract class CapacitorCommand extends Command { async getInstalledPlatforms(): Promise { const cli = await this.getCapacitorCLIConfig(); + const androidPlatformDirAbs = cli?.android.platformDirAbs ?? path.resolve(this.integration.root, 'android'); + const iosPlatformDirAbs = cli?.ios.platformDirAbs ?? path.resolve(this.integration.root, 'ios'); const platforms: string[] = []; - if (!cli) { - return []; - } - - if (await pathExists(cli.android.platformDirAbs)) { + if (await pathExists(androidPlatformDirAbs)) { platforms.push('android'); } - if (await pathExists(cli.ios.platformDirAbs)) { + if (await pathExists(iosPlatformDirAbs)) { platforms.push('ios'); } return platforms; } + async isPlatformInstalled(platform: string): Promise { + const platforms = await this.getInstalledPlatforms(); + + return platforms.includes(platform); + } + async checkCapacitor(runinfo: CommandInstanceInfo) { if (!this.project) { throw new FatalException(`Cannot use Capacitor outside a project directory.`); @@ -236,16 +240,6 @@ export abstract class CapacitorCommand extends Command { } } - async isPlatformInstalled(platform: string): Promise { - const cli = await this.getCapacitorCLIConfig(); - - if (!cli || (platform !== 'android' && platform !== 'ios')) { - return false; - } - - return await pathExists(cli[platform].platformDirAbs); - } - async checkForPlatformInstallation(platform: string) { if (!this.project) { throw new FatalException('Cannot use Capacitor outside a project directory.'); From e877dd8bbea685adbc0035f7f5afa13bbe111fb6 Mon Sep 17 00:00:00 2001 From: Daniel Imhoff Date: Mon, 28 Dec 2020 12:13:12 -0800 Subject: [PATCH 27/34] fix extConfig usage for backwards compat --- .../@ionic/cli/src/commands/capacitor/base.ts | 15 ++++++++++----- packages/@ionic/cli/src/lib/project/index.ts | 4 ++-- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/packages/@ionic/cli/src/commands/capacitor/base.ts b/packages/@ionic/cli/src/commands/capacitor/base.ts index 861f6138f6..676c74f603 100644 --- a/packages/@ionic/cli/src/commands/capacitor/base.ts +++ b/packages/@ionic/cli/src/commands/capacitor/base.ts @@ -12,7 +12,7 @@ import { FatalException, RunnerException } from '../../lib/errors'; import { runCommand } from '../../lib/executor'; import type { CapacitorCLIConfig, Integration as CapacitorIntegration } from '../../lib/integrations/capacitor' import { ANDROID_MANIFEST_FILE, CapacitorAndroidManifest } from '../../lib/integrations/capacitor/android'; -import { CAPACITOR_CONFIG_JSON_FILE, CapacitorJSONConfig } from '../../lib/integrations/capacitor/config'; +import { CAPACITOR_CONFIG_JSON_FILE, CapacitorJSONConfig, CapacitorConfig } from '../../lib/integrations/capacitor/config'; import { generateOptionsForCapacitorBuild } from '../../lib/integrations/capacitor/utils'; import { createPrefixedWriteStream } from '../../lib/utils/logger'; import { pkgManagerArgs } from '../../lib/utils/npm'; @@ -84,6 +84,12 @@ export abstract class CapacitorCommand extends Command { return capacitor.getCapacitorCLIConfig(); } + async getCapacitorConfig(): Promise { + const capacitor = await this.getCapacitorIntegration(); + + return capacitor.getCapacitorConfig(); + } + getCapacitorIntegration = lodash.memoize(async (): Promise => { if (!this.project) { throw new FatalException(`Cannot use Capacitor outside a project directory.`); @@ -169,9 +175,9 @@ export abstract class CapacitorCommand extends Command { throw new FatalException(`Cannot use Capacitor outside a project directory.`); } - const cli = await this.getCapacitorCLIConfig(); + const conf = await this.getCapacitorConfig(); - if (cli?.app.extConfig.server?.url) { + if (conf?.server?.url) { this.env.log.warn( `Capacitor server URL is in use.\n` + `This may result in unexpected behavior for this build, where an external server is used in the Web View instead of your app. This likely occurred because of ${input('--livereload')} usage in the past and the CLI improperly exiting without cleaning up.\n\n` + @@ -277,8 +283,7 @@ export abstract class CapacitorCommand extends Command { protected async createOptionsFromCommandLine(inputs: CommandLineInputs, options: CommandLineOptions): Promise { const separatedArgs = options['--']; const verbose = !!options['verbose']; - const capacitor = await this.getCapacitorIntegration(); - const conf = await capacitor.getCapacitorConfig(); + const conf = await this.getCapacitorConfig(); return { '--': separatedArgs ? separatedArgs : [], diff --git a/packages/@ionic/cli/src/lib/project/index.ts b/packages/@ionic/cli/src/lib/project/index.ts index 7c101081f6..4f285f53bc 100644 --- a/packages/@ionic/cli/src/lib/project/index.ts +++ b/packages/@ionic/cli/src/lib/project/index.ts @@ -549,8 +549,8 @@ export abstract class Project implements IProject { async getDistDir(): Promise { if (this.getIntegration('capacitor') !== undefined) { const capacitor = await this.createIntegration('capacitor'); - const conf = await capacitor.getCapacitorCLIConfig(); - const webDir = conf?.app.extConfig.webDir; + const conf = await capacitor.getCapacitorConfig(); + const webDir = conf?.webDir; if (webDir) { return path.resolve(this.directory, webDir); From 4be315bcee69dc56206bb9dd77c9013a5ceea569 Mon Sep 17 00:00:00 2001 From: Daniel Imhoff Date: Mon, 28 Dec 2020 12:14:13 -0800 Subject: [PATCH 28/34] use integration root for web dir --- packages/@ionic/cli/src/lib/project/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@ionic/cli/src/lib/project/index.ts b/packages/@ionic/cli/src/lib/project/index.ts index 4f285f53bc..349123f31e 100644 --- a/packages/@ionic/cli/src/lib/project/index.ts +++ b/packages/@ionic/cli/src/lib/project/index.ts @@ -553,7 +553,7 @@ export abstract class Project implements IProject { const webDir = conf?.webDir; if (webDir) { - return path.resolve(this.directory, webDir); + return path.resolve(capacitor.root, webDir); } else { throw new FatalException( `The ${input('webDir')} property must be set in the Capacitor configuration file. \n` + From 20b4b60c19bc733fba903ab2a95256d72d194e2b Mon Sep 17 00:00:00 2001 From: Daniel Imhoff Date: Mon, 28 Dec 2020 14:06:09 -0800 Subject: [PATCH 29/34] add ios and android to info --- packages/@ionic/cli/src/commands/start.ts | 1 - packages/@ionic/cli/src/lib/index.ts | 4 ++-- .../src/lib/integrations/capacitor/index.ts | 22 +++++++++++++++++-- 3 files changed, 22 insertions(+), 5 deletions(-) diff --git a/packages/@ionic/cli/src/commands/start.ts b/packages/@ionic/cli/src/commands/start.ts index 1944cda42c..44f3e2e54e 100644 --- a/packages/@ionic/cli/src/commands/start.ts +++ b/packages/@ionic/cli/src/commands/start.ts @@ -539,7 +539,6 @@ Use the ${input('--type')} option to start projects using older versions of Ioni this.env.shell.alterPath = p => prependNodeModulesBinToPath(projectDir, p); if (!this.schema.cloned) { - // Default to capacitor always if (this.schema.type === 'react' || this.schema.type === 'vue') { options['capacitor'] = true; } diff --git a/packages/@ionic/cli/src/lib/index.ts b/packages/@ionic/cli/src/lib/index.ts index adca575734..1effce3290 100644 --- a/packages/@ionic/cli/src/lib/index.ts +++ b/packages/@ionic/cli/src/lib/index.ts @@ -82,14 +82,14 @@ export async function generateIonicEnvironment(ctx: IonicContext, pargv: string[ group: 'utility', name: 'native-run', key: 'native_run_version', - value: nativeRun || 'not installed', + value: nativeRun || 'not installed globally', flair: nativeRunUpdate ? `update available: ${latestNativeRun ? success(latestNativeRun.version) : '???'}` : '', }, { group: 'utility', name: 'cordova-res', key: 'cordova_res_version', - value: cordovaRes || 'not installed', + value: cordovaRes || 'not installed globally', flair: cordovaResUpdate ? `update available: ${latestCordovaRes ? success(latestCordovaRes.version) : '???'}` : '', }, ]; diff --git a/packages/@ionic/cli/src/lib/integrations/capacitor/index.ts b/packages/@ionic/cli/src/lib/integrations/capacitor/index.ts index 3dba79fe2e..4256c8ff4a 100644 --- a/packages/@ionic/cli/src/lib/integrations/capacitor/index.ts +++ b/packages/@ionic/cli/src/lib/integrations/capacitor/index.ts @@ -1,4 +1,4 @@ -import { PackageJson, parseArgs } from '@ionic/cli-framework'; +import { parseArgs } from '@ionic/cli-framework'; import { mkdirp, pathExists } from '@ionic/utils-fs'; import { prettyPath } from '@ionic/utils-terminal'; import * as chalk from 'chalk'; @@ -125,9 +125,13 @@ export class Integration extends BaseIntegration { const [ [ capacitorCorePkg, capacitorCorePkgPath ], capacitorCLIVersion, - ] = await (Promise.all<[PackageJson | undefined, string | undefined], string | undefined>([ + [ capacitorIOSPkg, capacitorIOSPkgPath ], + [ capacitorAndroidPkg, capacitorAndroidPkgPath ], + ] = await (Promise.all([ this.e.project.getPackageJson('@capacitor/core'), this.getCapacitorCLIVersion(), + this.e.project.getPackageJson('@capacitor/ios'), + this.e.project.getPackageJson('@capacitor/android'), ])); const info: InfoItem[] = [ @@ -144,6 +148,20 @@ export class Integration extends BaseIntegration { value: capacitorCorePkg ? capacitorCorePkg.version : 'not installed', path: capacitorCorePkgPath, }, + { + group: 'capacitor', + name: '@capacitor/ios', + key: 'capacitor_ios_version', + value: capacitorIOSPkg ? capacitorIOSPkg.version : 'not installed', + path: capacitorIOSPkgPath, + }, + { + group: 'capacitor', + name: '@capacitor/android', + key: 'capacitor_android_version', + value: capacitorAndroidPkg ? capacitorAndroidPkg.version : 'not installed', + path: capacitorAndroidPkgPath, + }, { group: 'capacitor', name: 'Bundle ID', From fcae45d794c179424023c5acd719a2be9449b1b1 Mon Sep 17 00:00:00 2001 From: Daniel Imhoff Date: Mon, 28 Dec 2020 14:12:43 -0800 Subject: [PATCH 30/34] new apps use @next --- packages/@ionic/cli/src/commands/capacitor/base.ts | 2 +- packages/@ionic/cli/src/lib/integrations/capacitor/index.ts | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/@ionic/cli/src/commands/capacitor/base.ts b/packages/@ionic/cli/src/commands/capacitor/base.ts index 676c74f603..19a64199fa 100644 --- a/packages/@ionic/cli/src/commands/capacitor/base.ts +++ b/packages/@ionic/cli/src/commands/capacitor/base.ts @@ -273,7 +273,7 @@ export abstract class CapacitorCommand extends Command { } if (semver.gte(version, '3.0.0-alpha.1')) { - const [ manager, ...managerArgs ] = await pkgManagerArgs(this.env.config.get('npmClient'), { command: 'install', pkg: `@capacitor/${platform}`, saveDev: true }); + const [ manager, ...managerArgs ] = await pkgManagerArgs(this.env.config.get('npmClient'), { command: 'install', pkg: `@capacitor/${platform}@next`, saveDev: true }); await this.env.shell.run(manager, managerArgs, { cwd: this.integration.root }); } diff --git a/packages/@ionic/cli/src/lib/integrations/capacitor/index.ts b/packages/@ionic/cli/src/lib/integrations/capacitor/index.ts index 4256c8ff4a..e8b19b6691 100644 --- a/packages/@ionic/cli/src/lib/integrations/capacitor/index.ts +++ b/packages/@ionic/cli/src/lib/integrations/capacitor/index.ts @@ -81,7 +81,6 @@ export class Integration extends BaseIntegration { } options.push('--web-dir', webDir); - options.push('--npm-client', this.e.config.get('npmClient')); await this.installCapacitorCore(); await this.installCapacitorCLI(); @@ -98,12 +97,12 @@ export class Integration extends BaseIntegration { } async installCapacitorCore() { - const [ manager, ...managerArgs ] = await pkgManagerArgs(this.e.config.get('npmClient'), { command: 'install', pkg: '@capacitor/core' }); + const [ manager, ...managerArgs ] = await pkgManagerArgs(this.e.config.get('npmClient'), { command: 'install', pkg: '@capacitor/core@next' }); await this.e.shell.run(manager, managerArgs, { cwd: this.root }); } async installCapacitorCLI() { - const [ manager, ...managerArgs ] = await pkgManagerArgs(this.e.config.get('npmClient'), { command: 'install', pkg: '@capacitor/cli', saveDev: true }); + const [ manager, ...managerArgs ] = await pkgManagerArgs(this.e.config.get('npmClient'), { command: 'install', pkg: '@capacitor/cli@next', saveDev: true }); await this.e.shell.run(manager, managerArgs, { cwd: this.root }); } From bbcaf74846f10fde10cc485d2151126d8ec7f2ee Mon Sep 17 00:00:00 2001 From: Daniel Imhoff Date: Thu, 14 Jan 2021 13:16:05 -0800 Subject: [PATCH 31/34] only load manifest for android??? --- packages/@ionic/cli/src/commands/capacitor/base.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/@ionic/cli/src/commands/capacitor/base.ts b/packages/@ionic/cli/src/commands/capacitor/base.ts index 19a64199fa..023d6dd87e 100644 --- a/packages/@ionic/cli/src/commands/capacitor/base.ts +++ b/packages/@ionic/cli/src/commands/capacitor/base.ts @@ -227,9 +227,9 @@ export abstract class CapacitorCommand extends Command { conf.setServerUrl(serverUrl); - const manifest = await this.getAndroidManifest(); - if (platform === 'android') { + const manifest = await this.getAndroidManifest(); + onBeforeExit(async () => { await manifest.reset(); }); From 9b88d3a9de545881364e87f99b00a520e257d7c0 Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Fri, 14 May 2021 16:10:32 +0200 Subject: [PATCH 32/34] fix(capacitor): dont create capacitor.config.json if it doesn't exist Closes #4690 --- .../@ionic/cli/src/lib/integrations/capacitor/index.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/@ionic/cli/src/lib/integrations/capacitor/index.ts b/packages/@ionic/cli/src/lib/integrations/capacitor/index.ts index e8b19b6691..ba085c8515 100644 --- a/packages/@ionic/cli/src/lib/integrations/capacitor/index.ts +++ b/packages/@ionic/cli/src/lib/integrations/capacitor/index.ts @@ -108,12 +108,14 @@ export class Integration extends BaseIntegration { async personalize({ name, packageId }: ProjectPersonalizationDetails) { const confPath = this.getCapacitorConfigJsonPath(); - const conf = new CapacitorJSONConfig(confPath); + if (await pathExists(confPath)) { + const conf = new CapacitorJSONConfig(confPath); - conf.set('appName', name); + conf.set('appName', name); - if (packageId) { - conf.set('appId', packageId); + if (packageId) { + conf.set('appId', packageId); + } } } From eb9b748c1ad37a2bd1da5a9e2473234309d29881 Mon Sep 17 00:00:00 2001 From: Mike Hartington Date: Mon, 17 May 2021 13:04:20 -0400 Subject: [PATCH 33/34] feat(capacitor): add core capacitor plugins for ionic --- .../@ionic/cli/src/lib/integrations/capacitor/index.ts | 7 +++++++ packages/@ionic/cli/src/lib/utils/npm.ts | 9 +++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/packages/@ionic/cli/src/lib/integrations/capacitor/index.ts b/packages/@ionic/cli/src/lib/integrations/capacitor/index.ts index ba085c8515..599c315d7e 100644 --- a/packages/@ionic/cli/src/lib/integrations/capacitor/index.ts +++ b/packages/@ionic/cli/src/lib/integrations/capacitor/index.ts @@ -84,6 +84,7 @@ export class Integration extends BaseIntegration { await this.installCapacitorCore(); await this.installCapacitorCLI(); + await this.installCapacitorPlugins() await mkdirp(details.root); await this.e.shell.run('capacitor', ['init', name, packageId, ...options], { cwd: details.root }); @@ -106,6 +107,12 @@ export class Integration extends BaseIntegration { await this.e.shell.run(manager, managerArgs, { cwd: this.root }); } + async installCapacitorPlugins() { + const [ manager, ...managerArgs ] = await pkgManagerArgs(this.e.config.get('npmClient'), { command: 'install', pkg: ['@capacitor/haptics', '@capacitor/app', '@capacitor/keyboard'] }); + console.log(managerArgs) + await this.e.shell.run(manager, managerArgs, { cwd: this.root }); + } + async personalize({ name, packageId }: ProjectPersonalizationDetails) { const confPath = this.getCapacitorConfigJsonPath(); if (await pathExists(confPath)) { diff --git a/packages/@ionic/cli/src/lib/utils/npm.ts b/packages/@ionic/cli/src/lib/utils/npm.ts index c517417665..7cab7b862e 100644 --- a/packages/@ionic/cli/src/lib/utils/npm.ts +++ b/packages/@ionic/cli/src/lib/utils/npm.ts @@ -24,7 +24,7 @@ export type PkgManagerCommand = 'dedupe' | 'rebuild' | 'install' | 'uninstall' | export interface PkgManagerOptions { command: PkgManagerCommand; - pkg?: string; + pkg?: string | string[]; script?: string; scriptArgs?: string[]; global?: boolean; @@ -134,7 +134,12 @@ export async function pkgManagerArgs(npmClient: NpmClient, options: PkgManagerOp } if (options.pkg) { - installerArgs.push(options.pkg); + if (typeof options.pkg === 'string'){ + installerArgs.push(options.pkg); + } + if (Array.isArray(options.pkg)){ + installerArgs.push(...options.pkg) + } } if (cmd === 'run' && options.script) { From 43c6e60f92c8db1ebfd48654f5c0ad59c5299cc8 Mon Sep 17 00:00:00 2001 From: Mike Hartington Date: Tue, 18 May 2021 16:02:24 -0400 Subject: [PATCH 34/34] chore(): update the latest tags --- packages/@ionic/cli/src/lib/integrations/capacitor/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/@ionic/cli/src/lib/integrations/capacitor/index.ts b/packages/@ionic/cli/src/lib/integrations/capacitor/index.ts index 599c315d7e..e6e0e716ad 100644 --- a/packages/@ionic/cli/src/lib/integrations/capacitor/index.ts +++ b/packages/@ionic/cli/src/lib/integrations/capacitor/index.ts @@ -98,12 +98,12 @@ export class Integration extends BaseIntegration { } async installCapacitorCore() { - const [ manager, ...managerArgs ] = await pkgManagerArgs(this.e.config.get('npmClient'), { command: 'install', pkg: '@capacitor/core@next' }); + const [ manager, ...managerArgs ] = await pkgManagerArgs(this.e.config.get('npmClient'), { command: 'install', pkg: '@capacitor/core@latest' }); await this.e.shell.run(manager, managerArgs, { cwd: this.root }); } async installCapacitorCLI() { - const [ manager, ...managerArgs ] = await pkgManagerArgs(this.e.config.get('npmClient'), { command: 'install', pkg: '@capacitor/cli@next', saveDev: true }); + const [ manager, ...managerArgs ] = await pkgManagerArgs(this.e.config.get('npmClient'), { command: 'install', pkg: '@capacitor/cli@latest', saveDev: true }); await this.e.shell.run(manager, managerArgs, { cwd: this.root }); }