common.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.parseApkNameFromFlavor = exports.checkJDKMajorVersion = exports.resolvePlatform = exports.checkPlatformVersions = exports.getAddedPlatforms = exports.getPlatformTargetName = exports.promptForPlatformTarget = exports.promptForPlatform = exports.isValidEnterprisePlatform = exports.getKnownEnterprisePlatforms = exports.isValidCommunityPlatform = exports.getKnownCommunityPlatforms = exports.isValidPlatform = exports.getKnownPlatforms = exports.selectPlatforms = exports.getProjectPlatformDirectory = exports.getCLIVersion = exports.getCoreVersion = exports.getCapacitorPackageVersion = exports.requireCapacitorPackage = exports.getCapacitorPackage = exports.runTask = exports.runPlatformHook = exports.runHooks = exports.wait = exports.checkAppName = exports.checkAppId = exports.checkAppDir = exports.checkAppConfig = exports.checkCapacitorPlatform = exports.checkPackage = exports.checkWebDir = exports.check = void 0;
  4. const tslib_1 = require("tslib");
  5. const utils_fs_1 = require("@ionic/utils-fs");
  6. const utils_terminal_1 = require("@ionic/utils-terminal");
  7. const path_1 = require("path");
  8. const colors_1 = tslib_1.__importDefault(require("./colors"));
  9. const errors_1 = require("./errors");
  10. const log_1 = require("./log");
  11. const plugin_1 = require("./plugin");
  12. const monorepotools_1 = require("./util/monorepotools");
  13. const node_1 = require("./util/node");
  14. const subprocess_1 = require("./util/subprocess");
  15. async function check(checks) {
  16. const results = await Promise.all(checks.map(f => f()));
  17. const errors = results.filter(r => r != null);
  18. if (errors.length > 0) {
  19. throw errors.join('\n');
  20. }
  21. }
  22. exports.check = check;
  23. async function checkWebDir(config) {
  24. var _a;
  25. // We can skip checking the web dir if a server URL is set.
  26. if ((_a = config.app.extConfig.server) === null || _a === void 0 ? void 0 : _a.url) {
  27. return null;
  28. }
  29. const invalidFolders = ['', '.', '..', '../', './'];
  30. if (invalidFolders.includes(config.app.webDir)) {
  31. return `"${config.app.webDir}" is not a valid value for webDir`;
  32. }
  33. if (!(await (0, utils_fs_1.pathExists)(config.app.webDirAbs))) {
  34. return (`Could not find the web assets directory: ${colors_1.default.strong((0, utils_terminal_1.prettyPath)(config.app.webDirAbs))}.\n` +
  35. `Please create it and make sure it has an ${colors_1.default.strong('index.html')} file. You can change the path of this directory in ${colors_1.default.strong(config.app.extConfigName)} (${colors_1.default.input('webDir')} option). You may need to compile the web assets for your app (typically ${colors_1.default.input('npm run build')}). More info: ${colors_1.default.strong('https://capacitorjs.com/docs/basics/workflow#sync-your-project')}`);
  36. }
  37. if (!(await (0, utils_fs_1.pathExists)((0, path_1.join)(config.app.webDirAbs, 'index.html')))) {
  38. return (`The web assets directory (${colors_1.default.strong((0, utils_terminal_1.prettyPath)(config.app.webDirAbs))}) must contain an ${colors_1.default.strong('index.html')} file.\n` +
  39. `It will be the entry point for the web portion of the Capacitor app.`);
  40. }
  41. return null;
  42. }
  43. exports.checkWebDir = checkWebDir;
  44. async function checkPackage() {
  45. if (!(await (0, utils_fs_1.pathExists)('package.json'))) {
  46. if (await (0, utils_fs_1.pathExists)('project.json')) {
  47. return null;
  48. }
  49. else {
  50. return (`The Capacitor CLI needs to run at the root of an npm package or in a valid NX monorepo.\n` +
  51. `Make sure you have a package.json or project.json file in the directory where you run the Capacitor CLI.\n` +
  52. `More info: ${colors_1.default.strong('https://docs.npmjs.com/cli/init')}`);
  53. }
  54. }
  55. return null;
  56. }
  57. exports.checkPackage = checkPackage;
  58. async function checkCapacitorPlatform(config, platform) {
  59. const pkg = await getCapacitorPackage(config, platform);
  60. if (!pkg) {
  61. return (`Could not find the ${colors_1.default.input(platform)} platform.\n` +
  62. `You must install it in your project first, e.g. w/ ${colors_1.default.input(`npm install @capacitor/${platform}`)}`);
  63. }
  64. return null;
  65. }
  66. exports.checkCapacitorPlatform = checkCapacitorPlatform;
  67. async function checkAppConfig(config) {
  68. if (!config.app.appId) {
  69. return (`Missing ${colors_1.default.input('appId')} for new platform.\n` +
  70. `Please add it in ${config.app.extConfigName} or run ${colors_1.default.input('npx cap init')}.`);
  71. }
  72. if (!config.app.appName) {
  73. return (`Missing ${colors_1.default.input('appName')} for new platform.\n` +
  74. `Please add it in ${config.app.extConfigName} or run ${colors_1.default.input('npx cap init')}.`);
  75. }
  76. const appIdError = await checkAppId(config, config.app.appId);
  77. if (appIdError) {
  78. return appIdError;
  79. }
  80. const appNameError = await checkAppName(config, config.app.appName);
  81. if (appNameError) {
  82. return appNameError;
  83. }
  84. return null;
  85. }
  86. exports.checkAppConfig = checkAppConfig;
  87. async function checkAppDir(config, dir) {
  88. if (!/^\S*$/.test(dir)) {
  89. return `Your app directory should not contain spaces`;
  90. }
  91. return null;
  92. }
  93. exports.checkAppDir = checkAppDir;
  94. async function checkAppId(config, id) {
  95. if (!id) {
  96. return `Invalid App ID. Must be in Java package form with no dashes (ex: com.example.app)`;
  97. }
  98. if (/^[a-z][a-z0-9_]*(\.[a-z0-9_]+)+$/.test(id.toLowerCase())) {
  99. return null;
  100. }
  101. return `Invalid App ID "${id}". Must be in Java package form with no dashes (ex: com.example.app)`;
  102. }
  103. exports.checkAppId = checkAppId;
  104. async function checkAppName(config, name) {
  105. // We allow pretty much anything right now, have fun
  106. if (!(name === null || name === void 0 ? void 0 : name.length)) {
  107. return `Must provide an app name. For example: 'Spacebook'`;
  108. }
  109. return null;
  110. }
  111. exports.checkAppName = checkAppName;
  112. async function wait(time) {
  113. return new Promise(resolve => setTimeout(resolve, time));
  114. }
  115. exports.wait = wait;
  116. async function runHooks(config, platformName, dir, hook) {
  117. await runPlatformHook(config, platformName, dir, hook);
  118. const allPlugins = await (0, plugin_1.getPlugins)(config, platformName);
  119. allPlugins.forEach(async (p) => {
  120. await runPlatformHook(config, platformName, p.rootPath, hook);
  121. });
  122. }
  123. exports.runHooks = runHooks;
  124. async function runPlatformHook(config, platformName, platformDir, hook) {
  125. var _a;
  126. const { spawn } = await Promise.resolve().then(() => tslib_1.__importStar(require('child_process')));
  127. let pkg;
  128. if ((0, monorepotools_1.isNXMonorepo)(platformDir)) {
  129. pkg = await (0, utils_fs_1.readJSON)((0, path_1.join)((0, monorepotools_1.findNXMonorepoRoot)(platformDir), 'package.json'));
  130. }
  131. else {
  132. pkg = await (0, utils_fs_1.readJSON)((0, path_1.join)(platformDir, 'package.json'));
  133. }
  134. const cmd = (_a = pkg.scripts) === null || _a === void 0 ? void 0 : _a[hook];
  135. if (!cmd) {
  136. return;
  137. }
  138. return new Promise((resolve, reject) => {
  139. const p = spawn(cmd, {
  140. stdio: 'inherit',
  141. shell: true,
  142. cwd: platformDir,
  143. env: {
  144. INIT_CWD: platformDir,
  145. CAPACITOR_ROOT_DIR: config.app.rootDir,
  146. CAPACITOR_WEB_DIR: config.app.webDirAbs,
  147. CAPACITOR_CONFIG: JSON.stringify(config.app.extConfig),
  148. CAPACITOR_PLATFORM_NAME: platformName,
  149. ...process.env,
  150. },
  151. });
  152. p.on('close', () => {
  153. resolve();
  154. });
  155. p.on('error', err => {
  156. reject(err);
  157. });
  158. });
  159. }
  160. exports.runPlatformHook = runPlatformHook;
  161. async function runTask(title, fn) {
  162. const chain = log_1.output.createTaskChain();
  163. chain.next(title);
  164. try {
  165. const value = await fn();
  166. chain.end();
  167. return value;
  168. }
  169. catch (e) {
  170. chain.fail();
  171. throw e;
  172. }
  173. }
  174. exports.runTask = runTask;
  175. async function getCapacitorPackage(config, name) {
  176. const packagePath = (0, node_1.resolveNode)(config.app.rootDir, `@capacitor/${name}`, 'package.json');
  177. if (!packagePath) {
  178. return null;
  179. }
  180. return (0, utils_fs_1.readJSON)(packagePath);
  181. }
  182. exports.getCapacitorPackage = getCapacitorPackage;
  183. async function requireCapacitorPackage(config, name) {
  184. const pkg = await getCapacitorPackage(config, name);
  185. if (!pkg) {
  186. (0, errors_1.fatal)(`Unable to find node_modules/@capacitor/${name}.\n` +
  187. `Are you sure ${colors_1.default.strong(`@capacitor/${name}`)} is installed?`);
  188. }
  189. return pkg;
  190. }
  191. exports.requireCapacitorPackage = requireCapacitorPackage;
  192. async function getCapacitorPackageVersion(config, platform) {
  193. return (await requireCapacitorPackage(config, platform)).version;
  194. }
  195. exports.getCapacitorPackageVersion = getCapacitorPackageVersion;
  196. async function getCoreVersion(config) {
  197. return getCapacitorPackageVersion(config, 'core');
  198. }
  199. exports.getCoreVersion = getCoreVersion;
  200. async function getCLIVersion(config) {
  201. return getCapacitorPackageVersion(config, 'cli');
  202. }
  203. exports.getCLIVersion = getCLIVersion;
  204. function getPlatformDirectory(config, platform) {
  205. switch (platform) {
  206. case 'android':
  207. return config.android.platformDirAbs;
  208. case 'ios':
  209. return config.ios.platformDirAbs;
  210. case 'web':
  211. return config.web.platformDirAbs;
  212. }
  213. return null;
  214. }
  215. async function getProjectPlatformDirectory(config, platform) {
  216. const platformPath = getPlatformDirectory(config, platform);
  217. if (platformPath && (await (0, utils_fs_1.pathExists)(platformPath))) {
  218. return platformPath;
  219. }
  220. return null;
  221. }
  222. exports.getProjectPlatformDirectory = getProjectPlatformDirectory;
  223. async function selectPlatforms(config, selectedPlatformName) {
  224. if (selectedPlatformName) {
  225. // already passed in a platform name
  226. const platformName = selectedPlatformName.toLowerCase().trim();
  227. if (!(await isValidPlatform(platformName))) {
  228. (0, errors_1.fatal)(`Invalid platform: ${colors_1.default.input(platformName)}`);
  229. }
  230. else if (!(await getProjectPlatformDirectory(config, platformName))) {
  231. if (platformName === 'web') {
  232. (0, errors_1.fatal)(`Could not find the web platform directory.\n` +
  233. `Make sure ${colors_1.default.strong(config.app.webDir)} exists.`);
  234. }
  235. (0, errors_1.fatal)(`${colors_1.default.strong(platformName)} platform has not been added yet.\n` +
  236. `See the docs for adding the ${colors_1.default.strong(platformName)} platform: ${colors_1.default.strong(`https://capacitorjs.com/docs/${platformName}#adding-the-${platformName}-platform`)}`);
  237. }
  238. // return the platform in an string array
  239. return [platformName];
  240. }
  241. // wasn't given a platform name, so let's
  242. // get the platforms that have already been created
  243. return getAddedPlatforms(config);
  244. }
  245. exports.selectPlatforms = selectPlatforms;
  246. async function getKnownPlatforms() {
  247. return ['web', 'android', 'ios'];
  248. }
  249. exports.getKnownPlatforms = getKnownPlatforms;
  250. async function isValidPlatform(platform) {
  251. return (await getKnownPlatforms()).includes(platform);
  252. }
  253. exports.isValidPlatform = isValidPlatform;
  254. async function getKnownCommunityPlatforms() {
  255. return ['electron'];
  256. }
  257. exports.getKnownCommunityPlatforms = getKnownCommunityPlatforms;
  258. async function isValidCommunityPlatform(platform) {
  259. return (await getKnownCommunityPlatforms()).includes(platform);
  260. }
  261. exports.isValidCommunityPlatform = isValidCommunityPlatform;
  262. async function getKnownEnterprisePlatforms() {
  263. return ['windows'];
  264. }
  265. exports.getKnownEnterprisePlatforms = getKnownEnterprisePlatforms;
  266. async function isValidEnterprisePlatform(platform) {
  267. return (await getKnownEnterprisePlatforms()).includes(platform);
  268. }
  269. exports.isValidEnterprisePlatform = isValidEnterprisePlatform;
  270. async function promptForPlatform(platforms, promptMessage, selectedPlatformName) {
  271. const { prompt } = await Promise.resolve().then(() => tslib_1.__importStar(require('prompts')));
  272. if (!selectedPlatformName) {
  273. const answers = await prompt([
  274. {
  275. type: 'select',
  276. name: 'mode',
  277. message: promptMessage,
  278. choices: platforms.map(p => ({ title: p, value: p })),
  279. },
  280. ], { onCancel: () => process.exit(1) });
  281. return answers.mode.toLowerCase().trim();
  282. }
  283. const platformName = selectedPlatformName.toLowerCase().trim();
  284. if (!(await isValidPlatform(platformName))) {
  285. const knownPlatforms = await getKnownPlatforms();
  286. (0, errors_1.fatal)(`Invalid platform: ${colors_1.default.input(platformName)}.\n` +
  287. `Valid platforms include: ${knownPlatforms.join(', ')}`);
  288. }
  289. return platformName;
  290. }
  291. exports.promptForPlatform = promptForPlatform;
  292. async function promptForPlatformTarget(targets, selectedTarget) {
  293. const { prompt } = await Promise.resolve().then(() => tslib_1.__importStar(require('prompts')));
  294. const validTargets = targets.filter(t => t.id !== undefined);
  295. if (!selectedTarget) {
  296. if (validTargets.length === 1) {
  297. return validTargets[0];
  298. }
  299. else {
  300. const answers = await prompt([
  301. {
  302. type: 'select',
  303. name: 'target',
  304. message: 'Please choose a target device:',
  305. choices: validTargets.map(t => ({
  306. title: `${getPlatformTargetName(t)} (${t.id})`,
  307. value: t,
  308. })),
  309. },
  310. ], { onCancel: () => process.exit(1) });
  311. return answers.target;
  312. }
  313. }
  314. const targetID = selectedTarget.trim();
  315. const target = targets.find(t => t.id === targetID);
  316. if (!target) {
  317. (0, errors_1.fatal)(`Invalid target ID: ${colors_1.default.input(targetID)}.\n` +
  318. `Valid targets are: ${targets.map(t => t.id).join(', ')}`);
  319. }
  320. return target;
  321. }
  322. exports.promptForPlatformTarget = promptForPlatformTarget;
  323. function getPlatformTargetName(target) {
  324. var _a, _b, _c;
  325. return `${(_c = (_b = (_a = target.name) !== null && _a !== void 0 ? _a : target.model) !== null && _b !== void 0 ? _b : target.id) !== null && _c !== void 0 ? _c : '?'}${target.virtual
  326. ? ` (${target.platform === 'ios' ? 'simulator' : 'emulator'})`
  327. : ''}`;
  328. }
  329. exports.getPlatformTargetName = getPlatformTargetName;
  330. async function getAddedPlatforms(config) {
  331. const platforms = [];
  332. if (await getProjectPlatformDirectory(config, config.android.name)) {
  333. platforms.push(config.android.name);
  334. }
  335. if (await getProjectPlatformDirectory(config, config.ios.name)) {
  336. platforms.push(config.ios.name);
  337. }
  338. platforms.push(config.web.name);
  339. return platforms;
  340. }
  341. exports.getAddedPlatforms = getAddedPlatforms;
  342. async function checkPlatformVersions(config, platform) {
  343. const semver = await Promise.resolve().then(() => tslib_1.__importStar(require('semver')));
  344. const coreVersion = await getCoreVersion(config);
  345. const platformVersion = await getCapacitorPackageVersion(config, platform);
  346. if (semver.diff(coreVersion, platformVersion) === 'minor' ||
  347. semver.diff(coreVersion, platformVersion) === 'major') {
  348. log_1.logger.warn(`${colors_1.default.strong('@capacitor/core')}${colors_1.default.weak(`@${coreVersion}`)} version doesn't match ${colors_1.default.strong(`@capacitor/${platform}`)}${colors_1.default.weak(`@${platformVersion}`)} version.\n` +
  349. `Consider updating to a matching version, e.g. w/ ${colors_1.default.input(`npm install @capacitor/core@${platformVersion}`)}`);
  350. }
  351. }
  352. exports.checkPlatformVersions = checkPlatformVersions;
  353. function resolvePlatform(config, platform) {
  354. if (platform[0] !== '@') {
  355. const core = (0, node_1.resolveNode)(config.app.rootDir, `@capacitor/${platform}`, 'package.json');
  356. if (core) {
  357. return (0, path_1.dirname)(core);
  358. }
  359. const community = (0, node_1.resolveNode)(config.app.rootDir, `@capacitor-community/${platform}`, 'package.json');
  360. if (community) {
  361. return (0, path_1.dirname)(community);
  362. }
  363. const enterprise = (0, node_1.resolveNode)(config.app.rootDir, `@ionic-enterprise/capacitor-${platform}`, 'package.json');
  364. if (enterprise) {
  365. return (0, path_1.dirname)(enterprise);
  366. }
  367. }
  368. // third-party
  369. const thirdParty = (0, node_1.resolveNode)(config.app.rootDir, platform, 'package.json');
  370. if (thirdParty) {
  371. return (0, path_1.dirname)(thirdParty);
  372. }
  373. return null;
  374. }
  375. exports.resolvePlatform = resolvePlatform;
  376. async function checkJDKMajorVersion() {
  377. try {
  378. const string = await (0, subprocess_1.runCommand)('java', ['--version']);
  379. const versionRegex = RegExp(/([0-9]+)\.?([0-9]*)\.?([0-9]*)/);
  380. const versionMatch = versionRegex.exec(string);
  381. if (versionMatch === null) {
  382. return -1;
  383. }
  384. const firstVersionNumber = parseInt(versionMatch[1]);
  385. const secondVersionNumber = parseInt(versionMatch[2]);
  386. if (typeof firstVersionNumber === 'number' && firstVersionNumber != 1) {
  387. return firstVersionNumber;
  388. }
  389. else if (typeof secondVersionNumber === 'number' &&
  390. firstVersionNumber == 1 &&
  391. secondVersionNumber < 9) {
  392. return secondVersionNumber;
  393. }
  394. else {
  395. return -1;
  396. }
  397. }
  398. catch (e) {
  399. return -1;
  400. }
  401. }
  402. exports.checkJDKMajorVersion = checkJDKMajorVersion;
  403. function parseApkNameFromFlavor(flavor) {
  404. const convertedName = flavor.replace(/([A-Z])/g, '$1').toLowerCase();
  405. return `app-${convertedName ? `${convertedName}-` : ''}debug.apk`;
  406. }
  407. exports.parseApkNameFromFlavor = parseApkNameFromFlavor;