"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.RunCommand = void 0;
const cli_framework_1 = require("@ionic/cli-framework");
const utils_process_1 = require("@ionic/utils-process");
const chalk = require("chalk");
const lodash = require("lodash");
const color_1 = require("../../lib/color");
const errors_1 = require("../../lib/errors");
const hooks_1 = require("../../lib/hooks");
const utils_1 = require("../../lib/integrations/capacitor/utils");
const serve_1 = require("../../lib/serve");
const base_1 = require("./base");
class RunCommand extends base_1.CapacitorCommand {
async getMetadata() {
const groups = ["beta" /* BETA */];
const exampleCommands = [
'',
'android',
'android -l --external',
'ios --livereload --external',
'ios --livereload-url=http://localhost:8100',
].sort();
let options = [
// Build Options
{
name: 'build',
summary: 'Do not invoke Ionic build',
type: Boolean,
default: true,
},
{
name: 'open',
summary: 'Do not invoke Capacitor open',
type: Boolean,
default: true,
},
...serve_1.COMMON_SERVE_COMMAND_OPTIONS.filter(o => !['livereload'].includes(o.name)).map(o => ({ ...o, hint: color_1.weak('(--livereload)') })),
{
name: 'livereload',
summary: 'Spin up dev server to live-reload www files',
type: Boolean,
aliases: ['l'],
},
{
name: 'livereload-url',
summary: 'Provide a custom URL to the dev server',
spec: { value: 'url' },
},
];
const footnotes = [
{
id: 'remote-debugging-docs',
url: 'https://ionicframework.com/docs/developer-resources/developer-tips',
shortUrl: 'https://ion.link/remote-debugging-docs',
},
];
const serveRunner = this.project && await this.project.getServeRunner();
const buildRunner = this.project && await this.project.getBuildRunner();
if (buildRunner) {
const libmetadata = await buildRunner.getCommandMetadata();
groups.push(...libmetadata.groups || []);
options.push(...libmetadata.options || []);
footnotes.push(...libmetadata.footnotes || []);
}
if (serveRunner) {
const libmetadata = await serveRunner.getCommandMetadata();
const existingOpts = options.map(o => o.name);
groups.push(...libmetadata.groups || []);
const runnerOpts = (libmetadata.options || [])
.filter(o => !existingOpts.includes(o.name))
.map(o => ({ ...o, hint: `${o.hint ? `${o.hint} ` : ''}${color_1.weak('(--livereload)')}` }));
options = lodash.uniqWith([...runnerOpts, ...options], (optionA, optionB) => optionA.name === optionB.name);
footnotes.push(...libmetadata.footnotes || []);
}
return {
name: 'run',
type: 'project',
summary: 'Run an Ionic project on a connected device',
description: `
${color_1.input('ionic capacitor run')} will do the following:
- Perform ${color_1.input('ionic build')} (or run the dev server from ${color_1.input('ionic serve')} with the ${color_1.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)
When using the ${color_1.input('--livereload')} option and need to serve to your LAN, a device, or an emulator, use the ${color_1.input('--external')} option also. Otherwise, the web view tries to access ${color_1.input('localhost')}.
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.
For Android and iOS, you can setup Remote Debugging on your device with browser development tools using these docs[^remote-debugging-docs].
`,
footnotes,
exampleCommands,
inputs: [
{
name: 'platform',
summary: `The platform to run (e.g. ${['android', 'ios'].map(v => color_1.input(v)).join(', ')})`,
validators: [cli_framework_1.validators.required],
},
],
options,
groups,
};
}
async preRun(inputs, options, runinfo) {
await this.preRunChecks(runinfo);
if (!inputs[0]) {
const platform = await this.env.prompt({
type: 'list',
name: 'platform',
message: 'What platform would you like to run?',
choices: ['android', 'ios'],
});
inputs[0] = platform.trim();
}
if (options['livereload-url']) {
options['livereload'] = true;
}
if (!options['build'] && options['livereload']) {
this.env.log.warn(`No livereload with ${color_1.input('--no-build')}.`);
options['livereload'] = false;
}
await this.checkForPlatformInstallation(inputs[0]);
}
async run(inputs, options) {
if (!this.project) {
throw new errors_1.FatalException(`Cannot run ${color_1.input('ionic capacitor run/emulate')} outside a project directory.`);
}
const [platform] = inputs;
try {
if (options['livereload']) {
await this.runServe(inputs, options);
}
else {
await this.runBuild(inputs, options);
}
}
catch (e) {
if (e instanceof errors_1.RunnerException) {
throw new errors_1.FatalException(e.message);
}
throw e;
}
// copy assets and capacitor.config.json into place
await this.runCapacitor(['copy', platform]);
// TODO: native-run
const 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 (options['livereload']) {
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 utils_process_1.sleepForever();
}
}
async runServe(inputs, options) {
if (!this.project) {
throw new errors_1.FatalException(`Cannot run ${color_1.input('ionic capacitor run/emulate')} outside a project directory.`);
}
const [platform] = inputs;
const runner = await this.project.requireServeRunner();
const runnerOpts = runner.createOptionsFromCommandLine(inputs, utils_1.generateOptionsForCapacitorBuild(inputs, options));
let serverUrl = options['livereload-url'] ? String(options['livereload-url']) : undefined;
if (!serverUrl) {
const details = await runner.run(runnerOpts);
if (details.externallyAccessible === false) {
const extra = serve_1.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 ${color_1.strong(details.externalAddress)}.${extra}\n\n`);
}
serverUrl = `${details.protocol || 'http'}://${details.externalAddress}:${details.port}`;
}
const conf = this.getCapacitorConfig();
utils_process_1.onBeforeExit(async () => {
conf.resetServerUrl();
await this.runCapacitor(['copy', platform]);
});
conf.setServerUrl(serverUrl);
}
getContinueMessage(platform) {
if (platform === 'electron') {
return 'Ready to be used in Electron!';
}
return ('Ready for use in your Native IDE!\n' +
`To continue, run your project on a device or ${utils_1.getVirtualDeviceNameForPlatform(platform)} using ${utils_1.getNativeIDEForPlatform(platform)}!`);
}
async runCapacitorRunHook(name, inputs, options, e) {
const hook = new CapacitorRunHook(name, e);
let serveOptions;
let buildOptions;
if (options['livereload']) {
const serveRunner = await e.project.requireServeRunner();
serveOptions = serveRunner.createOptionsFromCommandLine(inputs, options);
}
else {
const buildRunner = await e.project.requireBuildRunner();
buildOptions = buildRunner.createOptionsFromCommandLine(inputs, options);
}
try {
await hook.run({
name: hook.name,
serve: serveOptions,
build: buildOptions,
capacitor: this.createOptionsFromCommandLine(inputs, options),
});
}
catch (e) {
if (e instanceof cli_framework_1.BaseError) {
throw new errors_1.FatalException(e.message);
}
throw e;
}
}
}
exports.RunCommand = RunCommand;
class CapacitorRunHook extends hooks_1.Hook {
constructor(name, e) {
super(e);
this.name = name;
}
}