|
| 1 | +import chalk from 'chalk'; |
| 2 | +import * as path from 'path'; |
| 3 | + |
| 4 | +import { validators } from '@ionic/cli-framework'; |
| 5 | +import { onBeforeExit, sleepForever } from '@ionic/cli-framework/utils/process'; |
| 6 | +import { CommandInstanceInfo, CommandLineInputs, CommandLineOptions, CommandMetadata, CommandMetadataOption, CommandPreRun } from '@ionic/cli-utils'; |
| 7 | +import { build } from '@ionic/cli-utils/lib/build'; |
| 8 | +import { FatalException } from '@ionic/cli-utils/lib/errors'; |
| 9 | +import { CAPACITOR_CONFIG_FILE, CapacitorConfig } from '@ionic/cli-utils/lib/integrations/capacitor/config'; |
| 10 | +import { generateOptionsForCapacitorBuild } from '@ionic/cli-utils/lib/integrations/capacitor/utils'; |
| 11 | +import { COMMON_SERVE_COMMAND_OPTIONS, LOCAL_ADDRESSES, serve } from '@ionic/cli-utils/lib/serve'; |
| 12 | + |
| 13 | +import { CapacitorCommand } from './base'; |
| 14 | + |
| 15 | +export class RunCommand extends CapacitorCommand implements CommandPreRun { |
| 16 | + async getMetadata(): Promise<CommandMetadata> { |
| 17 | + let groups: string[] = []; |
| 18 | + const options: CommandMetadataOption[] = [ |
| 19 | + // Build Options |
| 20 | + { |
| 21 | + name: 'build', |
| 22 | + summary: 'Do not invoke Ionic build', |
| 23 | + type: Boolean, |
| 24 | + default: true, |
| 25 | + }, |
| 26 | + ...COMMON_SERVE_COMMAND_OPTIONS.filter(o => !['livereload'].includes(o.name)), |
| 27 | + { |
| 28 | + name: 'livereload', |
| 29 | + summary: 'Spin up dev server to live-reload www files', |
| 30 | + type: Boolean, |
| 31 | + aliases: ['l'], |
| 32 | + }, |
| 33 | + ]; |
| 34 | + |
| 35 | + const serveRunner = this.project && await this.project.getServeRunner(); |
| 36 | + const buildRunner = this.project && await this.project.getBuildRunner(); |
| 37 | + |
| 38 | + if (buildRunner) { |
| 39 | + const libmetadata = await buildRunner.getCommandMetadata(); |
| 40 | + groups = libmetadata.groups || []; |
| 41 | + options.push(...libmetadata.options || []); |
| 42 | + } |
| 43 | + |
| 44 | + if (serveRunner) { |
| 45 | + const libmetadata = await serveRunner.getCommandMetadata(); |
| 46 | + const existingOpts = options.map(o => o.name); |
| 47 | + groups = libmetadata.groups || []; |
| 48 | + options.push(...(libmetadata.options || []).filter(o => !existingOpts.includes(o.name)).map(o => ({ ...o, hint: `${o.hint ? `${o.hint} ` : ''}${chalk.dim('(--livereload)')}` }))); |
| 49 | + } |
| 50 | + |
| 51 | + return { |
| 52 | + name: 'run', |
| 53 | + type: 'project', |
| 54 | + summary: 'Copies web assets to each Capacitor native platform', |
| 55 | + description: ` |
| 56 | +${chalk.green('ionic capacitor run')} will do the following: |
| 57 | +- Perform ${chalk.green('ionic build')} (or run the dev server from ${chalk.green('ionic serve')} with the ${chalk.green('--livereload')} option) |
| 58 | +- Copy web assets into the specified native platform |
| 59 | +
|
| 60 | +For Android and iOS, you can setup Remote Debugging on your device with browser development tools using these docs${chalk.cyan('[1]')}. |
| 61 | +
|
| 62 | +${chalk.cyan('[1]')}: ${chalk.bold('https://ionicframework.com/docs/developer-resources/developer-tips/')} |
| 63 | + `, |
| 64 | + exampleCommands: [ |
| 65 | + '', |
| 66 | + '-l', |
| 67 | + ], |
| 68 | + inputs: [ |
| 69 | + { |
| 70 | + name: 'platform', |
| 71 | + summary: `The platform to run (e.g. ${['android', 'ios'].map(v => chalk.green(v)).join(', ')})`, |
| 72 | + validators: [validators.required], |
| 73 | + }, |
| 74 | + ], |
| 75 | + options, |
| 76 | + groups, |
| 77 | + }; |
| 78 | + } |
| 79 | + |
| 80 | + async preRun(inputs: CommandLineInputs, options: CommandLineOptions, runinfo: CommandInstanceInfo): Promise<void> { |
| 81 | + await this.preRunChecks(runinfo); |
| 82 | + |
| 83 | + if (!inputs[0]) { |
| 84 | + const platform = await this.env.prompt({ |
| 85 | + type: 'list', |
| 86 | + name: 'platform', |
| 87 | + message: 'What platform would you like to run?', |
| 88 | + choices: ['android', 'ios'], |
| 89 | + }); |
| 90 | + |
| 91 | + inputs[0] = platform.trim(); |
| 92 | + } |
| 93 | + } |
| 94 | + |
| 95 | + async run(inputs: CommandLineInputs, options: CommandLineOptions): Promise<void> { |
| 96 | + if (!this.project) { |
| 97 | + throw new FatalException(`Cannot run ${chalk.green('ionic cordova run/emulate')} outside a project directory.`); |
| 98 | + } |
| 99 | + |
| 100 | + const [ platform ] = inputs; |
| 101 | + |
| 102 | + if (options['livereload']) { |
| 103 | + const conf = new CapacitorConfig(path.resolve(this.project.directory, CAPACITOR_CONFIG_FILE)); |
| 104 | + |
| 105 | + // TODO: use runner directly |
| 106 | + const details = await serve({ flags: this.env.flags, config: this.env.config, log: this.env.log, prompt: this.env.prompt, shell: this.env.shell, project: this.project }, inputs, generateOptionsForCapacitorBuild(inputs, options)); |
| 107 | + |
| 108 | + if (details.externallyAccessible === false) { |
| 109 | + const extra = LOCAL_ADDRESSES.includes(details.externalAddress) ? '\nEnsure you have proper port forwarding setup from your device to your computer.' : ''; |
| 110 | + this.env.log.warn(`Your device or emulator may not be able to access ${chalk.bold(details.externalAddress)}.${extra}\n\n`); |
| 111 | + } |
| 112 | + |
| 113 | + conf.setServerUrl(`${details.protocol || 'http'}://${details.externalAddress}:${details.port}`); |
| 114 | + |
| 115 | + onBeforeExit(async () => { |
| 116 | + conf.resetServerUrl(); |
| 117 | + }); |
| 118 | + } else { |
| 119 | + if (options['build']) { |
| 120 | + // TODO: use runner directly |
| 121 | + await build({ config: this.env.config, log: this.env.log, shell: this.env.shell, prompt: this.env.prompt, project: this.project }, inputs, generateOptionsForCapacitorBuild(inputs, options)); |
| 122 | + } |
| 123 | + } |
| 124 | + |
| 125 | + // copy assets and capacitor.config.json into place |
| 126 | + await this.runCapacitor(['copy', platform]); |
| 127 | + |
| 128 | + // TODO: native-run |
| 129 | + |
| 130 | + this.env.log.nl(); |
| 131 | + this.env.log.info( |
| 132 | + 'Ready for use in your Native IDE!\n' + |
| 133 | + `To continue, run your project on a device or ${platform === 'ios' ? 'simulator' : 'emulator'} using ${platform === 'ios' ? 'Xcode' : 'Android Studio'}!` |
| 134 | + ); |
| 135 | + |
| 136 | + this.env.log.nl(); |
| 137 | + |
| 138 | + await this.runCapacitor(['open', platform]); |
| 139 | + |
| 140 | + if (options['livereload']) { |
| 141 | + this.env.log.nl(); |
| 142 | + this.env.log.info( |
| 143 | + 'Development server will continue running until manually stopped.\n' + |
| 144 | + chalk.yellow('Use Ctrl+C to quit this process') |
| 145 | + ); |
| 146 | + await sleepForever(); |
| 147 | + } |
| 148 | + } |
| 149 | +} |
0 commit comments