base.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.CapacitorCommand = void 0;
  4. const tslib_1 = require("tslib");
  5. const utils_fs_1 = require("@ionic/utils-fs");
  6. const utils_process_1 = require("@ionic/utils-process");
  7. const utils_subprocess_1 = require("@ionic/utils-subprocess");
  8. const lodash = tslib_1.__importStar(require("lodash"));
  9. const path = tslib_1.__importStar(require("path"));
  10. const semver = tslib_1.__importStar(require("semver"));
  11. const color_1 = require("../../lib/color");
  12. const command_1 = require("../../lib/command");
  13. const errors_1 = require("../../lib/errors");
  14. const executor_1 = require("../../lib/executor");
  15. const android_1 = require("../../lib/integrations/capacitor/android");
  16. const config_1 = require("../../lib/integrations/capacitor/config");
  17. const ios_1 = require("../../lib/integrations/capacitor/ios");
  18. const utils_1 = require("../../lib/integrations/capacitor/utils");
  19. const logger_1 = require("../../lib/utils/logger");
  20. const npm_1 = require("../../lib/utils/npm");
  21. class CapacitorCommand extends command_1.Command {
  22. constructor() {
  23. super(...arguments);
  24. this.getCapacitorIntegration = lodash.memoize(async () => {
  25. if (!this.project) {
  26. throw new errors_1.FatalException(`Cannot use Capacitor outside a project directory.`);
  27. }
  28. return this.project.createIntegration('capacitor');
  29. });
  30. this.getCapacitorVersion = lodash.memoize(async () => {
  31. try {
  32. const proc = await this.env.shell.createSubprocess('capacitor', ['--version'], { cwd: this.integration.root });
  33. const version = semver.parse((await proc.output()).trim());
  34. if (!version) {
  35. throw new errors_1.FatalException('Error while parsing Capacitor CLI version.');
  36. }
  37. return version;
  38. }
  39. catch (e) {
  40. if (e instanceof utils_subprocess_1.SubprocessError) {
  41. if (e.code === utils_subprocess_1.ERROR_COMMAND_NOT_FOUND) {
  42. throw new errors_1.FatalException('Error while getting Capacitor CLI version. Is Capacitor installed?');
  43. }
  44. throw new errors_1.FatalException('Error while getting Capacitor CLI version.\n' + (e.output ? e.output : e.code));
  45. }
  46. throw e;
  47. }
  48. });
  49. }
  50. get integration() {
  51. if (!this.project) {
  52. throw new errors_1.FatalException(`Cannot use Capacitor outside a project directory.`);
  53. }
  54. if (!this._integration) {
  55. this._integration = this.project.requireIntegration('capacitor');
  56. }
  57. return this._integration;
  58. }
  59. async getGeneratedConfig(platform) {
  60. if (!this.project) {
  61. throw new errors_1.FatalException(`Cannot use Capacitor outside a project directory.`);
  62. }
  63. const p = await this.getGeneratedConfigPath(platform);
  64. return new config_1.CapacitorJSONConfig(p);
  65. }
  66. async getGeneratedConfigPath(platform) {
  67. if (!this.project) {
  68. throw new errors_1.FatalException(`Cannot use Capacitor outside a project directory.`);
  69. }
  70. const p = await this.getGeneratedConfigDir(platform);
  71. return path.resolve(this.integration.root, p, config_1.CAPACITOR_CONFIG_JSON_FILE);
  72. }
  73. async getAndroidManifest() {
  74. const p = await this.getAndroidManifestPath();
  75. return android_1.CapacitorAndroidManifest.load(p);
  76. }
  77. async getAndroidManifestPath() {
  78. const cli = await this.getCapacitorCLIConfig();
  79. const srcDir = cli?.android.srcMainDirAbs ?? 'android/app/src/main';
  80. return path.resolve(this.integration.root, srcDir, android_1.ANDROID_MANIFEST_FILE);
  81. }
  82. async getiOSAppInfo() {
  83. const p = await this.getiOSAppInfoPath();
  84. return ios_1.CapacitorIosInfo.load(p);
  85. }
  86. async getiOSAppInfoPath() {
  87. const cli = await this.getCapacitorCLIConfig();
  88. const srcDir = cli?.ios.nativeTargetDirAbs ?? 'ios/App/App';
  89. return path.resolve(this.integration.root, srcDir, ios_1.IOS_INFO_FILE);
  90. }
  91. async getGeneratedConfigDir(platform) {
  92. const cli = await this.getCapacitorCLIConfig();
  93. switch (platform) {
  94. case 'android':
  95. return cli?.android.assetsDirAbs ?? 'android/app/src/main/assets';
  96. case 'ios':
  97. return cli?.ios.nativeTargetDirAbs ?? 'ios/App/App';
  98. }
  99. throw new errors_1.FatalException(`Could not determine generated Capacitor config path for ${(0, color_1.input)(platform)} platform.`);
  100. }
  101. async getCapacitorCLIConfig() {
  102. const capacitor = await this.getCapacitorIntegration();
  103. return capacitor.getCapacitorCLIConfig();
  104. }
  105. async getCapacitorConfig() {
  106. const capacitor = await this.getCapacitorIntegration();
  107. return capacitor.getCapacitorConfig();
  108. }
  109. isCorePlatform(platform) {
  110. const platforms = ['android', 'ios'];
  111. return platforms.includes(platform);
  112. }
  113. async getInstalledPlatforms() {
  114. const cli = await this.getCapacitorCLIConfig();
  115. const androidPlatformDirAbs = cli?.android.platformDirAbs ?? path.resolve(this.integration.root, 'android');
  116. const iosPlatformDirAbs = cli?.ios.platformDirAbs ?? path.resolve(this.integration.root, 'ios');
  117. const platforms = [];
  118. if (await (0, utils_fs_1.pathExists)(androidPlatformDirAbs)) {
  119. platforms.push('android');
  120. }
  121. if (await (0, utils_fs_1.pathExists)(iosPlatformDirAbs)) {
  122. platforms.push('ios');
  123. }
  124. if (await (0, utils_fs_1.pathExists)(path.resolve(this.integration.root, 'electron'))) {
  125. platforms.push('electron');
  126. }
  127. return platforms;
  128. }
  129. async isPlatformInstalled(platform) {
  130. const platforms = await this.getInstalledPlatforms();
  131. return platforms.includes(platform);
  132. }
  133. async checkCapacitor(runinfo) {
  134. if (!this.project) {
  135. throw new errors_1.FatalException(`Cannot use Capacitor outside a project directory.`);
  136. }
  137. const capacitor = this.project.getIntegration('capacitor');
  138. if (!capacitor) {
  139. await (0, executor_1.runCommand)(runinfo, ['integrations', 'enable', 'capacitor']);
  140. }
  141. }
  142. async preRunChecks(runinfo) {
  143. await this.checkCapacitor(runinfo);
  144. }
  145. async runCapacitor(argList) {
  146. if (!this.project) {
  147. throw new errors_1.FatalException(`Cannot use Capacitor outside a project directory.`);
  148. }
  149. const stream = (0, logger_1.createPrefixedWriteStream)(this.env.log, (0, color_1.weak)(`[capacitor]`));
  150. await this.env.shell.run('capacitor', argList, { stream, fatalOnNotFound: false, truncateErrorOutput: 5000, cwd: this.integration.root });
  151. }
  152. async runBuild(inputs, options) {
  153. if (!this.project) {
  154. throw new errors_1.FatalException(`Cannot use Capacitor outside a project directory.`);
  155. }
  156. const conf = await this.getCapacitorConfig();
  157. if (conf?.server?.url) {
  158. this.env.log.warn(`Capacitor server URL is in use.\n` +
  159. `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 ${(0, color_1.input)('--livereload')} usage in the past and the CLI improperly exiting without cleaning up.\n\n` +
  160. `Delete the ${(0, color_1.input)('server')} key in the Capacitor config file if you did not intend to use an external server.`);
  161. this.env.log.nl();
  162. }
  163. if (options['build']) {
  164. try {
  165. const runner = await this.project.requireBuildRunner();
  166. const runnerOpts = runner.createOptionsFromCommandLine(inputs, (0, utils_1.generateOptionsForCapacitorBuild)(inputs, options));
  167. await runner.run(runnerOpts);
  168. }
  169. catch (e) {
  170. if (e instanceof errors_1.RunnerException) {
  171. throw new errors_1.FatalException(e.message);
  172. }
  173. throw e;
  174. }
  175. }
  176. }
  177. async runServe(inputs, options) {
  178. if (!this.project) {
  179. throw new errors_1.FatalException(`Cannot run ${(0, color_1.input)('ionic capacitor run')} outside a project directory.`);
  180. }
  181. const [platform] = inputs;
  182. try {
  183. const runner = await this.project.requireServeRunner();
  184. const runnerOpts = runner.createOptionsFromCommandLine(inputs, (0, utils_1.generateOptionsForCapacitorBuild)(inputs, options));
  185. let serverUrl = options['livereload-url'] ? String(options['livereload-url']) : undefined;
  186. if (!serverUrl) {
  187. const details = await runner.run(runnerOpts);
  188. serverUrl = `${details.protocol || 'http'}://${details.externalAddress}:${details.port}`;
  189. }
  190. const conf = await this.getGeneratedConfig(platform);
  191. (0, utils_process_1.onBeforeExit)(async () => {
  192. conf.resetServerUrl();
  193. });
  194. conf.setServerUrl(serverUrl);
  195. if (platform === 'android') {
  196. const manifest = await this.getAndroidManifest();
  197. (0, utils_process_1.onBeforeExit)(async () => {
  198. await manifest.reset();
  199. });
  200. manifest.enableCleartextTraffic();
  201. await manifest.save();
  202. }
  203. if (platform === 'ios' && !options['external']) {
  204. const appInfo = await this.getiOSAppInfo();
  205. (0, utils_process_1.onBeforeExit)(async () => {
  206. await appInfo.reset();
  207. });
  208. appInfo.disableAppTransportSecurity();
  209. await appInfo.save();
  210. }
  211. }
  212. catch (e) {
  213. if (e instanceof errors_1.RunnerException) {
  214. throw new errors_1.FatalException(e.message);
  215. }
  216. throw e;
  217. }
  218. }
  219. async checkForPlatformInstallation(platform) {
  220. if (!this.project) {
  221. throw new errors_1.FatalException('Cannot use Capacitor outside a project directory.');
  222. }
  223. if (platform) {
  224. const capacitor = this.project.getIntegration('capacitor');
  225. if (!capacitor) {
  226. throw new errors_1.FatalException('Cannot check platform installations--Capacitor not yet integrated.');
  227. }
  228. if (!(await this.isPlatformInstalled(platform))) {
  229. await this.installPlatform(platform);
  230. }
  231. }
  232. }
  233. async installPlatform(platform) {
  234. const version = await this.getCapacitorVersion();
  235. const installedPlatforms = await this.getInstalledPlatforms();
  236. if (installedPlatforms.includes(platform)) {
  237. throw new errors_1.FatalException(`The ${(0, color_1.input)(platform)} platform is already installed!`);
  238. }
  239. if (semver.gte(version, '3.0.0-alpha.1')) {
  240. if (this.isCorePlatform(platform)) {
  241. const [manager, ...managerArgs] = await (0, npm_1.pkgManagerArgs)(this.env.config.get('npmClient'), { command: 'install', pkg: `@capacitor/${platform}@${version}`, saveDev: false });
  242. await this.env.shell.run(manager, managerArgs, { cwd: this.integration.root });
  243. }
  244. }
  245. await this.runCapacitor(['add', platform]);
  246. }
  247. async createOptionsFromCommandLine(inputs, options) {
  248. const separatedArgs = options['--'];
  249. const verbose = !!options['verbose'];
  250. const conf = await this.getCapacitorConfig();
  251. return {
  252. '--': separatedArgs ? separatedArgs : [],
  253. verbose,
  254. ...conf,
  255. };
  256. }
  257. }
  258. exports.CapacitorCommand = CapacitorCommand;