Add dependency
Some checks failed
/ build_android (push) Failing after 21s

This commit is contained in:
Pieter Vander Vennet 2025-06-18 18:50:46 +02:00
parent 55470c090d
commit 6947a1adba
1260 changed files with 111297 additions and 0 deletions

1
@capacitor/assets/node_modules/.bin/cap generated vendored Symbolic link
View file

@ -0,0 +1 @@
../@capacitor/cli/bin/capacitor

1
@capacitor/assets/node_modules/.bin/capacitor generated vendored Symbolic link
View file

@ -0,0 +1 @@
../@capacitor/cli/bin/capacitor

1
@capacitor/assets/node_modules/.bin/rimraf generated vendored Symbolic link
View file

@ -0,0 +1 @@
../rimraf/dist/cjs/src/bin.js

1
@capacitor/assets/node_modules/.bin/semver generated vendored Symbolic link
View file

@ -0,0 +1 @@
../semver/bin/semver.js

21
@capacitor/assets/node_modules/@capacitor/cli/LICENSE generated vendored Normal file
View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2017-present Drifty Co.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -0,0 +1,15 @@
# Capacitor CLI
The Capacitor command-line interface should be installed locally and executed through `npm` scripts.
```
npm install @capacitor/cli --save-dev
```
## Using Capacitor CLI
Consult the Getting Started guide for information on using the CLI and Capacitor scripts.
### License
* [MIT](https://github.com/ionic-team/capacitor/blob/HEAD/LICENSE)

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,52 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.createLocalProperties = exports.addAndroid = void 0;
const tslib_1 = require("tslib");
const utils_fs_1 = require("@ionic/utils-fs");
const os_1 = require("os");
const path_1 = require("path");
const colors_1 = tslib_1.__importDefault(require("../colors"));
const common_1 = require("../common");
const subprocess_1 = require("../util/subprocess");
const template_1 = require("../util/template");
async function addAndroid(config) {
await (0, common_1.runTask)(`Adding native android project in ${colors_1.default.strong(config.android.platformDir)}`, async () => {
return (0, template_1.extractTemplate)(config.cli.assets.android.platformTemplateArchiveAbs, config.android.platformDirAbs);
});
}
exports.addAndroid = addAndroid;
async function createLocalProperties(platformDir) {
const defaultAndroidPath = (0, path_1.join)((0, os_1.homedir)(), 'Library/Android/sdk');
if (await (0, utils_fs_1.pathExists)(defaultAndroidPath)) {
const localSettings = `
## This file is automatically generated by Android Studio.
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
#
# This file should *NOT* be checked into Version Control Systems,
# as it contains information specific to your local configuration.
#
# Location of the SDK. This is only used by Gradle.
# For customization when using a Version Control System, please read the
# header note.
sdk.dir=${defaultAndroidPath}
`;
await (0, utils_fs_1.writeFile)((0, path_1.join)(platformDir, 'local.properties'), localSettings, {
encoding: 'utf-8',
});
// Only sync if we were able to create the local properties above, otherwise
// this will fail
try {
await gradleSync(platformDir);
}
catch (e) {
console.error('Error running gradle sync', e);
console.error('Unable to infer default Android SDK settings. This is fine, just run npx cap open android and import and sync gradle manually');
}
}
}
exports.createLocalProperties = createLocalProperties;
async function gradleSync(platformDir) {
await (0, subprocess_1.runCommand)(`./gradlew`, [], {
cwd: platformDir,
});
}

View file

@ -0,0 +1,100 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.buildAndroid = void 0;
const tslib_1 = require("tslib");
const path_1 = require("path");
const colors_1 = tslib_1.__importDefault(require("../colors"));
const common_1 = require("../common");
const log_1 = require("../log");
const subprocess_1 = require("../util/subprocess");
async function buildAndroid(config, buildOptions) {
var _a, _b;
const releaseType = (_a = buildOptions.androidreleasetype) !== null && _a !== void 0 ? _a : 'AAB';
const releaseTypeIsAAB = releaseType === 'AAB';
const flavor = (_b = buildOptions.flavor) !== null && _b !== void 0 ? _b : '';
const arg = releaseTypeIsAAB
? `:app:bundle${flavor}Release`
: `assemble${flavor}Release`;
const gradleArgs = [arg];
try {
await (0, common_1.runTask)('Running Gradle build', async () => (0, subprocess_1.runCommand)('./gradlew', gradleArgs, {
cwd: config.android.platformDirAbs,
}));
}
catch (e) {
if (e.includes('EACCES')) {
throw `gradlew file does not have executable permissions. This can happen if the Android platform was added on a Windows machine. Please run ${colors_1.default.strong(`chmod +x ./${config.android.platformDir}/gradlew`)} and try again.`;
}
else {
throw e;
}
}
const releaseDir = releaseTypeIsAAB
? flavor !== ''
? `${flavor}Release`
: 'release'
: flavor !== ''
? (0, path_1.join)(flavor, 'release')
: 'release';
const releasePath = (0, path_1.join)(config.android.appDirAbs, 'build', 'outputs', releaseTypeIsAAB ? 'bundle' : 'apk', releaseDir);
const unsignedReleaseName = `app${flavor !== '' ? `-${flavor}` : ''}-release${releaseTypeIsAAB ? '' : '-unsigned'}.${releaseType.toLowerCase()}`;
const signedReleaseName = unsignedReleaseName.replace(`-release${releaseTypeIsAAB ? '' : '-unsigned'}.${releaseType.toLowerCase()}`, `-release-signed.${releaseType.toLowerCase()}`);
if (buildOptions.signingtype == 'jarsigner') {
await signWithJarSigner(config, buildOptions, releasePath, signedReleaseName, unsignedReleaseName);
}
else {
await signWithApkSigner(config, buildOptions, releasePath, signedReleaseName, unsignedReleaseName);
}
(0, log_1.logSuccess)(`Successfully generated ${signedReleaseName} at: ${releasePath}`);
}
exports.buildAndroid = buildAndroid;
async function signWithApkSigner(config, buildOptions, releasePath, signedReleaseName, unsignedReleaseName) {
if (!buildOptions.keystorepath || !buildOptions.keystorepass) {
throw 'Missing options. Please supply all options for android signing. (Keystore Path, Keystore Password)';
}
const signingArgs = [
'sign',
'--ks',
buildOptions.keystorepath,
'--ks-pass',
`pass:${buildOptions.keystorepass}`,
'--in',
`${(0, path_1.join)(releasePath, unsignedReleaseName)}`,
'--out',
`${(0, path_1.join)(releasePath, signedReleaseName)}`,
];
await (0, common_1.runTask)('Signing Release', async () => {
await (0, subprocess_1.runCommand)('apksigner', signingArgs, {
cwd: config.android.platformDirAbs,
});
});
}
async function signWithJarSigner(config, buildOptions, releasePath, signedReleaseName, unsignedReleaseName) {
if (!buildOptions.keystorepath ||
!buildOptions.keystorealias ||
!buildOptions.keystorealiaspass ||
!buildOptions.keystorepass) {
throw 'Missing options. Please supply all options for android signing. (Keystore Path, Keystore Password, Keystore Key Alias, Keystore Key Password)';
}
const signingArgs = [
'-sigalg',
'SHA1withRSA',
'-digestalg',
'SHA1',
'-keystore',
buildOptions.keystorepath,
'-keypass',
buildOptions.keystorealiaspass,
'-storepass',
buildOptions.keystorepass,
`-signedjar`,
`${(0, path_1.join)(releasePath, signedReleaseName)}`,
`${(0, path_1.join)(releasePath, unsignedReleaseName)}`,
buildOptions.keystorealias,
];
await (0, common_1.runTask)('Signing Release', async () => {
await (0, subprocess_1.runCommand)('jarsigner', signingArgs, {
cwd: config.android.platformDirAbs,
});
});
}

View file

@ -0,0 +1,97 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.editProjectSettingsAndroid = exports.resolvePlugin = exports.getAndroidPlugins = exports.checkAndroidPackage = void 0;
const utils_fs_1 = require("@ionic/utils-fs");
const path_1 = require("path");
const common_1 = require("../common");
const cordova_1 = require("../cordova");
const plugin_1 = require("../plugin");
const fs_1 = require("../util/fs");
async function checkAndroidPackage(config) {
return (0, common_1.checkCapacitorPlatform)(config, 'android');
}
exports.checkAndroidPackage = checkAndroidPackage;
async function getAndroidPlugins(allPlugins) {
const resolved = await Promise.all(allPlugins.map(async (plugin) => await resolvePlugin(plugin)));
return resolved.filter((plugin) => !!plugin);
}
exports.getAndroidPlugins = getAndroidPlugins;
async function resolvePlugin(plugin) {
var _a;
const platform = 'android';
if ((_a = plugin.manifest) === null || _a === void 0 ? void 0 : _a.android) {
let pluginFilesPath = plugin.manifest.android.src
? plugin.manifest.android.src
: platform;
const absolutePath = (0, path_1.join)(plugin.rootPath, pluginFilesPath, plugin.id);
// Android folder shouldn't have subfolders, but they used to, so search for them for compatibility reasons
if (await (0, utils_fs_1.pathExists)(absolutePath)) {
pluginFilesPath = (0, path_1.join)(platform, plugin.id);
}
plugin.android = {
type: 0 /* PluginType.Core */,
path: (0, fs_1.convertToUnixPath)(pluginFilesPath),
};
}
else if (plugin.xml) {
plugin.android = {
type: 1 /* PluginType.Cordova */,
path: 'src/' + platform,
};
if ((0, cordova_1.getIncompatibleCordovaPlugins)(platform).includes(plugin.id) ||
!(0, plugin_1.getPluginPlatform)(plugin, platform)) {
plugin.android.type = 2 /* PluginType.Incompatible */;
}
}
else {
return null;
}
return plugin;
}
exports.resolvePlugin = resolvePlugin;
/**
* Update an Android project with the desired app name and appId.
* This is a little trickier for Android because the appId becomes
* the package name.
*/
async function editProjectSettingsAndroid(config) {
const appId = config.app.appId;
const appName = config.app.appName
.replace(/&/g, '&')
.replace(/</g, '&lt;')
.replace(/"/g, '\\"')
.replace(/'/g, "\\'");
const buildGradlePath = (0, path_1.resolve)(config.android.appDirAbs, 'build.gradle');
const domainPath = appId.split('.').join('/');
// Make the package source path to the new plugin Java file
const newJavaPath = (0, path_1.resolve)(config.android.srcMainDirAbs, `java/${domainPath}`);
if (!(await (0, utils_fs_1.pathExists)(newJavaPath))) {
await (0, utils_fs_1.mkdirp)(newJavaPath);
}
await (0, utils_fs_1.copy)((0, path_1.resolve)(config.android.srcMainDirAbs, 'java/com/getcapacitor/myapp/MainActivity.java'), (0, path_1.resolve)(newJavaPath, 'MainActivity.java'));
if (appId.split('.')[1] !== 'getcapacitor') {
await (0, utils_fs_1.remove)((0, path_1.resolve)(config.android.srcMainDirAbs, 'java/com/getcapacitor'));
}
// Remove our template 'com' folder if their ID doesn't have it
if (appId.split('.')[0] !== 'com') {
await (0, utils_fs_1.remove)((0, path_1.resolve)(config.android.srcMainDirAbs, 'java/com/'));
}
// Update the package in the MainActivity java file
const activityPath = (0, path_1.resolve)(newJavaPath, 'MainActivity.java');
let activityContent = await (0, utils_fs_1.readFile)(activityPath, { encoding: 'utf-8' });
activityContent = activityContent.replace(/package ([^;]*)/, `package ${appId}`);
await (0, utils_fs_1.writeFile)(activityPath, activityContent, { encoding: 'utf-8' });
// Update the applicationId in build.gradle
let gradleContent = await (0, utils_fs_1.readFile)(buildGradlePath, { encoding: 'utf-8' });
gradleContent = gradleContent.replace(/applicationId "[^"]+"/, `applicationId "${appId}"`);
// Update the namespace in build.gradle
gradleContent = gradleContent.replace(/namespace "[^"]+"/, `namespace "${appId}"`);
await (0, utils_fs_1.writeFile)(buildGradlePath, gradleContent, { encoding: 'utf-8' });
// Update the settings in res/values/strings.xml
const stringsPath = (0, path_1.resolve)(config.android.resDirAbs, 'values/strings.xml');
let stringsContent = await (0, utils_fs_1.readFile)(stringsPath, { encoding: 'utf-8' });
stringsContent = stringsContent.replace(/com.getcapacitor.myapp/g, appId);
stringsContent = stringsContent.replace(/My App/g, appName);
await (0, utils_fs_1.writeFile)(stringsPath, stringsContent);
}
exports.editProjectSettingsAndroid = editProjectSettingsAndroid;

View file

@ -0,0 +1,163 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.doctorAndroid = void 0;
const tslib_1 = require("tslib");
const utils_fs_1 = require("@ionic/utils-fs");
const path_1 = require("path");
const colors_1 = tslib_1.__importDefault(require("../colors"));
const common_1 = require("../common");
const errors_1 = require("../errors");
const log_1 = require("../log");
const xml_1 = require("../util/xml");
async function doctorAndroid(config) {
var _a;
try {
await (0, common_1.check)([
checkAndroidInstalled,
() => checkGradlew(config),
() => checkAppSrcDirs(config),
]);
(0, log_1.logSuccess)('Android looking great! 👌');
}
catch (e) {
if (!(0, errors_1.isFatal)(e)) {
(0, errors_1.fatal)((_a = e.stack) !== null && _a !== void 0 ? _a : e);
}
throw e;
}
}
exports.doctorAndroid = doctorAndroid;
async function checkAppSrcDirs(config) {
if (!(await (0, utils_fs_1.pathExists)(config.android.appDirAbs))) {
return `${colors_1.default.strong(config.android.appDir)} directory is missing in ${colors_1.default.strong(config.android.platformDir)}`;
}
if (!(await (0, utils_fs_1.pathExists)(config.android.srcMainDirAbs))) {
return `${colors_1.default.strong(config.android.srcMainDir)} directory is missing in ${colors_1.default.strong(config.android.platformDir)}`;
}
if (!(await (0, utils_fs_1.pathExists)(config.android.assetsDirAbs))) {
return `${colors_1.default.strong(config.android.assetsDir)} directory is missing in ${colors_1.default.strong(config.android.platformDir)}`;
}
if (!(await (0, utils_fs_1.pathExists)(config.android.webDirAbs))) {
return `${colors_1.default.strong(config.android.webDir)} directory is missing in ${colors_1.default.strong(config.android.platformDir)}`;
}
const appSrcMainAssetsWwwIndexHtmlDir = (0, path_1.join)(config.android.webDirAbs, 'index.html');
if (!(await (0, utils_fs_1.pathExists)(appSrcMainAssetsWwwIndexHtmlDir))) {
return `${colors_1.default.strong('index.html')} file is missing in ${colors_1.default.strong(config.android.webDirAbs)}`;
}
return checkAndroidManifestFile(config);
}
async function checkAndroidManifestFile(config) {
const manifestFileName = 'AndroidManifest.xml';
const manifestFilePath = (0, path_1.join)(config.android.srcMainDirAbs, manifestFileName);
if (!(await (0, utils_fs_1.pathExists)(manifestFilePath))) {
return `${colors_1.default.strong(manifestFileName)} is missing in ${colors_1.default.strong(config.android.srcMainDir)}`;
}
try {
const xmlData = await (0, xml_1.readXML)(manifestFilePath);
return checkAndroidManifestData(config, xmlData);
}
catch (e) {
return e;
}
}
async function checkAndroidManifestData(config, xmlData) {
const manifestNode = xmlData.manifest;
if (!manifestNode) {
return `Missing ${colors_1.default.input('<manifest>')} XML node in ${colors_1.default.strong(config.android.srcMainDir)}`;
}
const applicationChildNodes = manifestNode.application;
if (!Array.isArray(manifestNode.application)) {
return `Missing ${colors_1.default.input('<application>')} XML node as a child node of ${colors_1.default.input('<manifest>')} in ${colors_1.default.strong(config.android.srcMainDir)}`;
}
let mainActivityClassPath = '';
const mainApplicationNode = applicationChildNodes.find(applicationChildNode => {
const activityChildNodes = applicationChildNode.activity;
if (!Array.isArray(activityChildNodes)) {
return false;
}
const mainActivityNode = activityChildNodes.find(activityChildNode => {
const intentFilterChildNodes = activityChildNode['intent-filter'];
if (!Array.isArray(intentFilterChildNodes)) {
return false;
}
return intentFilterChildNodes.find(intentFilterChildNode => {
const actionChildNodes = intentFilterChildNode.action;
if (!Array.isArray(actionChildNodes)) {
return false;
}
const mainActionChildNode = actionChildNodes.find(actionChildNode => {
const androidName = actionChildNode.$['android:name'];
return androidName === 'android.intent.action.MAIN';
});
if (!mainActionChildNode) {
return false;
}
const categoryChildNodes = intentFilterChildNode.category;
if (!Array.isArray(categoryChildNodes)) {
return false;
}
return categoryChildNodes.find(categoryChildNode => {
const androidName = categoryChildNode.$['android:name'];
return androidName === 'android.intent.category.LAUNCHER';
});
});
});
if (mainActivityNode) {
mainActivityClassPath = mainActivityNode.$['android:name'];
}
return mainActivityNode;
});
if (!mainApplicationNode) {
return `Missing main ${colors_1.default.input('<activity>')} XML node in ${colors_1.default.strong(config.android.srcMainDir)}`;
}
if (!mainActivityClassPath) {
return `Missing ${colors_1.default.input('<activity android:name="">')} attribute for MainActivity class in ${colors_1.default.strong(config.android.srcMainDir)}`;
}
return checkPackage(config, mainActivityClassPath);
}
async function checkPackage(config, mainActivityClassPath) {
const appSrcMainJavaDir = (0, path_1.join)(config.android.srcMainDirAbs, 'java');
if (!(await (0, utils_fs_1.pathExists)(appSrcMainJavaDir))) {
return `${colors_1.default.strong('java')} directory is missing in ${colors_1.default.strong(appSrcMainJavaDir)}`;
}
const mainActivityClassName = mainActivityClassPath.split('.').pop();
const srcFiles = await (0, utils_fs_1.readdirp)(appSrcMainJavaDir, {
filter: entry => !entry.stats.isDirectory() &&
['.java', '.kt'].includes((0, path_1.extname)(entry.path)) &&
mainActivityClassName === (0, path_1.parse)(entry.path).name,
});
if (srcFiles.length == 0) {
return `Main activity file (${mainActivityClassName}) is missing`;
}
return checkBuildGradle(config);
}
async function checkBuildGradle(config) {
const fileName = 'build.gradle';
const filePath = (0, path_1.join)(config.android.appDirAbs, fileName);
if (!(await (0, utils_fs_1.pathExists)(filePath))) {
return `${colors_1.default.strong(fileName)} file is missing in ${colors_1.default.strong(config.android.appDir)}`;
}
let fileContent = await (0, utils_fs_1.readFile)(filePath, { encoding: 'utf-8' });
fileContent = fileContent.replace(/'|"/g, '').replace(/\s+/g, ' ');
const searchFor = `applicationId`;
if (fileContent.indexOf(searchFor) === -1) {
return `${colors_1.default.strong('build.gradle')} file missing ${colors_1.default.input(`applicationId`)} config in ${filePath}`;
}
return null;
}
async function checkGradlew(config) {
const fileName = 'gradlew';
const filePath = (0, path_1.join)(config.android.platformDirAbs, fileName);
if (!(await (0, utils_fs_1.pathExists)(filePath))) {
return `${colors_1.default.strong(fileName)} file is missing in ${colors_1.default.strong(config.android.platformDir)}`;
}
return null;
}
async function checkAndroidInstalled() {
/*
if (!await isInstalled('android')) {
return 'Android is not installed. For information: https://developer.android.com/studio/index.html';
}
*/
return null;
}

View file

@ -0,0 +1,28 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.openAndroid = void 0;
const tslib_1 = require("tslib");
const utils_fs_1 = require("@ionic/utils-fs");
const debug_1 = tslib_1.__importDefault(require("debug"));
const open_1 = tslib_1.__importDefault(require("open"));
const colors_1 = tslib_1.__importDefault(require("../colors"));
const log_1 = require("../log");
const debug = (0, debug_1.default)('capacitor:android:open');
async function openAndroid(config) {
const androidStudioPath = await config.android.studioPath;
const dir = config.android.platformDirAbs;
try {
if (!(await (0, utils_fs_1.pathExists)(androidStudioPath))) {
throw new Error(`Android Studio does not exist at: ${androidStudioPath}`);
}
await (0, open_1.default)(dir, { app: { name: androidStudioPath }, wait: false });
log_1.logger.info(`Opening Android project at: ${colors_1.default.strong(config.android.platformDir)}.`);
}
catch (e) {
debug('Error opening Android Studio: %O', e);
log_1.logger.error('Unable to launch Android Studio. Is it installed?\n' +
`Attempted to open Android Studio at: ${colors_1.default.strong(androidStudioPath)}\n` +
`You can configure this with the ${colors_1.default.input('CAPACITOR_ANDROID_STUDIO_PATH')} environment variable.`);
}
}
exports.openAndroid = openAndroid;

View file

@ -0,0 +1,42 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.runAndroid = void 0;
const tslib_1 = require("tslib");
const debug_1 = tslib_1.__importDefault(require("debug"));
const path_1 = require("path");
const colors_1 = tslib_1.__importDefault(require("../colors"));
const common_1 = require("../common");
const native_run_1 = require("../util/native-run");
const subprocess_1 = require("../util/subprocess");
const debug = (0, debug_1.default)('capacitor:android:run');
async function runAndroid(config, { target: selectedTarget, flavor: selectedFlavor, forwardPorts: selectedPorts, }) {
var _a;
const target = await (0, common_1.promptForPlatformTarget)(await (0, native_run_1.getPlatformTargets)('android'), selectedTarget);
const runFlavor = selectedFlavor || ((_a = config.android) === null || _a === void 0 ? void 0 : _a.flavor) || '';
const arg = `assemble${runFlavor}Debug`;
const gradleArgs = [arg];
debug('Invoking ./gradlew with args: %O', gradleArgs);
try {
await (0, common_1.runTask)('Running Gradle build', async () => (0, subprocess_1.runCommand)('./gradlew', gradleArgs, {
cwd: config.android.platformDirAbs,
}));
}
catch (e) {
if (e.includes('EACCES')) {
throw `gradlew file does not have executable permissions. This can happen if the Android platform was added on a Windows machine. Please run ${colors_1.default.strong(`chmod +x ./${config.android.platformDir}/gradlew`)} and try again.`;
}
else {
throw e;
}
}
const pathToApk = `${config.android.platformDirAbs}/${config.android.appDir}/build/outputs/apk${runFlavor !== '' ? '/' + runFlavor : ''}/debug`;
const apkName = (0, common_1.parseApkNameFromFlavor)(runFlavor);
const apkPath = (0, path_1.resolve)(pathToApk, apkName);
const nativeRunArgs = ['android', '--app', apkPath, '--target', target.id];
if (selectedPorts) {
nativeRunArgs.push('--forward', `${selectedPorts}`);
}
debug('Invoking native-run with args: %O', nativeRunArgs);
await (0, common_1.runTask)(`Deploying ${colors_1.default.strong(apkName)} to ${colors_1.default.input(target.id)}`, async () => (0, native_run_1.runNativeRun)(nativeRunArgs));
}
exports.runAndroid = runAndroid;

View file

@ -0,0 +1,323 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.handleCordovaPluginsGradle = exports.installGradlePlugins = exports.updateAndroid = void 0;
const tslib_1 = require("tslib");
const utils_fs_1 = require("@ionic/utils-fs");
const debug_1 = tslib_1.__importDefault(require("debug"));
const path_1 = require("path");
const colors_1 = tslib_1.__importDefault(require("../colors"));
const common_1 = require("../common");
const cordova_1 = require("../cordova");
const errors_1 = require("../errors");
const plugin_1 = require("../plugin");
const copy_1 = require("../tasks/copy");
const migrate_1 = require("../tasks/migrate");
const fs_1 = require("../util/fs");
const node_1 = require("../util/node");
const template_1 = require("../util/template");
const common_2 = require("./common");
const platform = 'android';
const debug = (0, debug_1.default)('capacitor:android:update');
async function updateAndroid(config) {
const plugins = await getPluginsTask(config);
const capacitorPlugins = plugins.filter(p => (0, plugin_1.getPluginType)(p, platform) === 0 /* PluginType.Core */);
(0, plugin_1.printPlugins)(capacitorPlugins, 'android');
await writePluginsJson(config, capacitorPlugins);
await removePluginsNativeFiles(config);
const cordovaPlugins = plugins.filter(p => (0, plugin_1.getPluginType)(p, platform) === 1 /* PluginType.Cordova */);
await (0, migrate_1.patchOldCapacitorPlugins)(config);
if (cordovaPlugins.length > 0) {
await copyPluginsNativeFiles(config, cordovaPlugins);
}
if (!(await (0, utils_fs_1.pathExists)(config.android.webDirAbs))) {
await (0, copy_1.copy)(config, platform);
}
await (0, cordova_1.handleCordovaPluginsJS)(cordovaPlugins, config, platform);
await (0, cordova_1.checkPluginDependencies)(plugins, platform);
await installGradlePlugins(config, capacitorPlugins, cordovaPlugins);
await handleCordovaPluginsGradle(config, cordovaPlugins);
await (0, cordova_1.writeCordovaAndroidManifest)(cordovaPlugins, config, platform);
const incompatibleCordovaPlugins = plugins.filter(p => (0, plugin_1.getPluginType)(p, platform) === 2 /* PluginType.Incompatible */);
(0, plugin_1.printPlugins)(incompatibleCordovaPlugins, platform, 'incompatible');
await (0, common_1.checkPlatformVersions)(config, platform);
}
exports.updateAndroid = updateAndroid;
function getGradlePackageName(id) {
return id.replace('@', '').replace('/', '-');
}
async function writePluginsJson(config, plugins) {
const classes = await findAndroidPluginClasses(plugins);
const pluginsJsonPath = (0, path_1.resolve)(config.android.assetsDirAbs, 'capacitor.plugins.json');
await (0, utils_fs_1.writeJSON)(pluginsJsonPath, classes, { spaces: '\t' });
}
async function findAndroidPluginClasses(plugins) {
const entries = [];
for (const plugin of plugins) {
entries.push(...(await findAndroidPluginClassesInPlugin(plugin)));
}
return entries;
}
async function findAndroidPluginClassesInPlugin(plugin) {
if (!plugin.android || (0, plugin_1.getPluginType)(plugin, platform) !== 0 /* PluginType.Core */) {
return [];
}
const srcPath = (0, path_1.resolve)(plugin.rootPath, plugin.android.path, 'src/main');
const srcFiles = await (0, utils_fs_1.readdirp)(srcPath, {
filter: entry => !entry.stats.isDirectory() &&
['.java', '.kt'].includes((0, path_1.extname)(entry.path)),
});
const classRegex = /^@(?:CapacitorPlugin|NativePlugin)[\s\S]+?class ([\w]+)/gm;
const packageRegex = /^package ([\w.]+);?$/gm;
debug('Searching %O source files in %O by %O regex', srcFiles.length, srcPath, classRegex);
const entries = await Promise.all(srcFiles.map(async (srcFile) => {
const srcFileContents = await (0, utils_fs_1.readFile)(srcFile, { encoding: 'utf-8' });
classRegex.lastIndex = 0;
const classMatch = classRegex.exec(srcFileContents);
if (classMatch) {
const className = classMatch[1];
debug('Searching %O for package by %O regex', srcFile, packageRegex);
packageRegex.lastIndex = 0;
const packageMatch = packageRegex.exec(srcFileContents.substring(0, classMatch.index));
if (!packageMatch) {
(0, errors_1.fatal)(`Package could not be parsed from Android plugin.\n` +
`Location: ${colors_1.default.strong(srcFile)}`);
}
const packageName = packageMatch[1];
const classpath = `${packageName}.${className}`;
debug('%O is a suitable plugin class', classpath);
return {
pkg: plugin.id,
classpath,
};
}
}));
return entries.filter((entry) => !!entry);
}
async function installGradlePlugins(config, capacitorPlugins, cordovaPlugins) {
const capacitorAndroidPackagePath = (0, node_1.resolveNode)(config.app.rootDir, '@capacitor/android', 'package.json');
if (!capacitorAndroidPackagePath) {
(0, errors_1.fatal)(`Unable to find ${colors_1.default.strong('node_modules/@capacitor/android')}.\n` +
`Are you sure ${colors_1.default.strong('@capacitor/android')} is installed?`);
}
const capacitorAndroidPath = (0, path_1.resolve)((0, path_1.dirname)(capacitorAndroidPackagePath), 'capacitor');
const settingsPath = config.android.platformDirAbs;
const dependencyPath = config.android.appDirAbs;
const relativeCapcitorAndroidPath = (0, fs_1.convertToUnixPath)((0, path_1.relative)(settingsPath, capacitorAndroidPath));
const settingsLines = `// DO NOT EDIT THIS FILE! IT IS GENERATED EACH TIME "capacitor update" IS RUN
include ':capacitor-android'
project(':capacitor-android').projectDir = new File('${relativeCapcitorAndroidPath}')
${capacitorPlugins
.map(p => {
if (!p.android) {
return '';
}
const relativePluginPath = (0, fs_1.convertToUnixPath)((0, path_1.relative)(settingsPath, p.rootPath));
return `
include ':${getGradlePackageName(p.id)}'
project(':${getGradlePackageName(p.id)}').projectDir = new File('${relativePluginPath}/${p.android.path}')
`;
})
.join('')}`;
const applyArray = [];
const frameworksArray = [];
let prefsArray = [];
cordovaPlugins.map(p => {
const relativePluginPath = (0, fs_1.convertToUnixPath)((0, path_1.relative)(dependencyPath, p.rootPath));
const frameworks = (0, plugin_1.getPlatformElement)(p, platform, 'framework');
frameworks.map((framework) => {
if (framework.$.custom &&
framework.$.custom === 'true' &&
framework.$.type &&
framework.$.type === 'gradleReference') {
applyArray.push(`apply from: "${relativePluginPath}/${framework.$.src}"`);
}
else if (!framework.$.type && !framework.$.custom) {
if (framework.$.src.startsWith('platform(')) {
frameworksArray.push(` implementation ${framework.$.src}`);
}
else {
frameworksArray.push(` implementation "${framework.$.src}"`);
}
}
});
prefsArray = prefsArray.concat((0, plugin_1.getAllElements)(p, platform, 'preference'));
});
let frameworkString = frameworksArray.join('\n');
frameworkString = await replaceFrameworkVariables(config, prefsArray, frameworkString);
const dependencyLines = `// DO NOT EDIT THIS FILE! IT IS GENERATED EACH TIME "capacitor update" IS RUN
android {
compileOptions {
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
}
}
apply from: "../capacitor-cordova-android-plugins/cordova.variables.gradle"
dependencies {
${capacitorPlugins
.map(p => {
return ` implementation project(':${getGradlePackageName(p.id)}')`;
})
.join('\n')}
${frameworkString}
}
${applyArray.join('\n')}
if (hasProperty('postBuildExtras')) {
postBuildExtras()
}
`;
await (0, utils_fs_1.writeFile)((0, path_1.join)(settingsPath, 'capacitor.settings.gradle'), settingsLines);
await (0, utils_fs_1.writeFile)((0, path_1.join)(dependencyPath, 'capacitor.build.gradle'), dependencyLines);
}
exports.installGradlePlugins = installGradlePlugins;
async function handleCordovaPluginsGradle(config, cordovaPlugins) {
var _a, _b, _c;
const pluginsGradlePath = (0, path_1.join)(config.android.cordovaPluginsDirAbs, 'build.gradle');
const kotlinNeeded = await kotlinNeededCheck(config, cordovaPlugins);
const kotlinVersionString = (_c = (_b = (_a = config.app.extConfig.cordova) === null || _a === void 0 ? void 0 : _a.preferences) === null || _b === void 0 ? void 0 : _b.GradlePluginKotlinVersion) !== null && _c !== void 0 ? _c : '1.8.20';
const frameworksArray = [];
let prefsArray = [];
const applyArray = [];
applyArray.push(`apply from: "cordova.variables.gradle"`);
cordovaPlugins.map(p => {
const relativePluginPath = (0, fs_1.convertToUnixPath)((0, path_1.relative)(config.android.cordovaPluginsDirAbs, p.rootPath));
const frameworks = (0, plugin_1.getPlatformElement)(p, platform, 'framework');
frameworks.map((framework) => {
if (!framework.$.type && !framework.$.custom) {
frameworksArray.push(framework.$.src);
}
else if (framework.$.custom &&
framework.$.custom === 'true' &&
framework.$.type &&
framework.$.type === 'gradleReference') {
applyArray.push(`apply from: "${relativePluginPath}/${framework.$.src}"`);
}
});
prefsArray = prefsArray.concat((0, plugin_1.getAllElements)(p, platform, 'preference'));
});
let frameworkString = frameworksArray
.map(f => {
if (f.startsWith('platform(')) {
return ` implementation ${f}`;
}
else {
return ` implementation "${f}"`;
}
})
.join('\n');
frameworkString = await replaceFrameworkVariables(config, prefsArray, frameworkString);
if (kotlinNeeded) {
frameworkString += `\n implementation "androidx.core:core-ktx:$androidxCoreKTXVersion"`;
frameworkString += `\n implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"`;
}
const applyString = applyArray.join('\n');
let buildGradle = await (0, utils_fs_1.readFile)(pluginsGradlePath, { encoding: 'utf-8' });
buildGradle = buildGradle.replace(/(SUB-PROJECT DEPENDENCIES START)[\s\S]*(\/\/ SUB-PROJECT DEPENDENCIES END)/, '$1\n' + frameworkString.concat('\n') + ' $2');
buildGradle = buildGradle.replace(/(PLUGIN GRADLE EXTENSIONS START)[\s\S]*(\/\/ PLUGIN GRADLE EXTENSIONS END)/, '$1\n' + applyString.concat('\n') + '$2');
if (kotlinNeeded) {
buildGradle = buildGradle.replace(/(buildscript\s{\n(\t|\s{4})repositories\s{\n((\t{2}|\s{8}).+\n)+(\t|\s{4})}\n(\t|\s{4})dependencies\s{\n(\t{2}|\s{8}).+)\n((\t|\s{4})}\n}\n)/, `$1\n classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"\n$8`);
buildGradle = buildGradle.replace(/(ext\s{)/, `$1\n androidxCoreKTXVersion = project.hasProperty('androidxCoreKTXVersion') ? rootProject.ext.androidxCoreKTXVersion : '1.8.0'`);
buildGradle = buildGradle.replace(/(buildscript\s{)/, `$1\n ext.kotlin_version = project.hasProperty('kotlin_version') ? rootProject.ext.kotlin_version : '${kotlinVersionString}'`);
buildGradle = buildGradle.replace(/(apply\splugin:\s'com\.android\.library')/, `$1\napply plugin: 'kotlin-android'`);
buildGradle = buildGradle.replace(/(compileOptions\s{\n((\t{2}|\s{8}).+\n)+(\t|\s{4})})\n(})/, `$1\n sourceSets {\n main.java.srcDirs += 'src/main/kotlin'\n }\n$5`);
}
await (0, utils_fs_1.writeFile)(pluginsGradlePath, buildGradle);
const cordovaVariables = `// DO NOT EDIT THIS FILE! IT IS GENERATED EACH TIME "capacitor update" IS RUN
ext {
cdvMinSdkVersion = project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion : ${config.android.minVersion}
// Plugin gradle extensions can append to this to have code run at the end.
cdvPluginPostBuildExtras = []
cordovaConfig = [:]
}`;
await (0, utils_fs_1.writeFile)((0, path_1.join)(config.android.cordovaPluginsDirAbs, 'cordova.variables.gradle'), cordovaVariables);
}
exports.handleCordovaPluginsGradle = handleCordovaPluginsGradle;
async function kotlinNeededCheck(config, cordovaPlugins) {
var _a, _b;
if (((_b = (_a = config.app.extConfig.cordova) === null || _a === void 0 ? void 0 : _a.preferences) === null || _b === void 0 ? void 0 : _b.GradlePluginKotlinEnabled) !==
'true') {
for (const plugin of cordovaPlugins) {
const androidPlatform = (0, plugin_1.getPluginPlatform)(plugin, platform);
const sourceFiles = androidPlatform['source-file'];
if (sourceFiles) {
for (const srcFile of sourceFiles) {
if (/^.*\.kt$/.test(srcFile['$'].src)) {
return true;
}
}
}
}
return false;
}
else {
return true;
}
}
async function copyPluginsNativeFiles(config, cordovaPlugins) {
const pluginsPath = (0, path_1.join)(config.android.cordovaPluginsDirAbs, 'src', 'main');
for (const p of cordovaPlugins) {
const androidPlatform = (0, plugin_1.getPluginPlatform)(p, platform);
if (androidPlatform) {
const sourceFiles = androidPlatform['source-file'];
if (sourceFiles) {
for (const sourceFile of sourceFiles) {
const fileName = sourceFile.$.src.split('/').pop();
let baseFolder = 'java/';
if (fileName.split('.').pop() === 'aidl') {
baseFolder = 'aidl/';
}
const target = sourceFile.$['target-dir']
.replace('app/src/main/', '')
.replace('src/', baseFolder);
await (0, utils_fs_1.copy)((0, plugin_1.getFilePath)(config, p, sourceFile.$.src), (0, path_1.join)(pluginsPath, target, fileName));
}
}
const resourceFiles = androidPlatform['resource-file'];
if (resourceFiles) {
for (const resourceFile of resourceFiles) {
const target = resourceFile.$['target'];
if (resourceFile.$.src.split('.').pop() === 'aar') {
await (0, utils_fs_1.copy)((0, plugin_1.getFilePath)(config, p, resourceFile.$.src), (0, path_1.join)(pluginsPath, 'libs', target.split('/').pop()));
}
else if (target !== '.') {
await (0, utils_fs_1.copy)((0, plugin_1.getFilePath)(config, p, resourceFile.$.src), (0, path_1.join)(pluginsPath, target));
}
}
}
const libFiles = (0, plugin_1.getPlatformElement)(p, platform, 'lib-file');
for (const libFile of libFiles) {
await (0, utils_fs_1.copy)((0, plugin_1.getFilePath)(config, p, libFile.$.src), (0, path_1.join)(pluginsPath, 'libs', libFile.$.src.split('/').pop()));
}
}
}
}
async function removePluginsNativeFiles(config) {
await (0, utils_fs_1.remove)(config.android.cordovaPluginsDirAbs);
await (0, template_1.extractTemplate)(config.cli.assets.android.cordovaPluginsTemplateArchiveAbs, config.android.cordovaPluginsDirAbs);
}
async function getPluginsTask(config) {
return await (0, common_1.runTask)('Updating Android plugins', async () => {
const allPlugins = await (0, plugin_1.getPlugins)(config, 'android');
const androidPlugins = await (0, common_2.getAndroidPlugins)(allPlugins);
return androidPlugins;
});
}
async function getVariablesGradleFile(config) {
const variablesFile = (0, path_1.resolve)(config.android.platformDirAbs, 'variables.gradle');
let variablesGradle = '';
if (await (0, utils_fs_1.pathExists)(variablesFile)) {
variablesGradle = await (0, utils_fs_1.readFile)(variablesFile, { encoding: 'utf-8' });
}
return variablesGradle;
}
async function replaceFrameworkVariables(config, prefsArray, frameworkString) {
const variablesGradle = await getVariablesGradleFile(config);
prefsArray.map((preference) => {
if (!variablesGradle.includes(preference.$.name)) {
frameworkString = frameworkString.replace(new RegExp(('$' + preference.$.name).replace('$', '\\$&'), 'g'), preference.$.default);
}
});
return frameworkString;
}

View file

@ -0,0 +1,26 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ancillary = exports.failure = exports.success = exports.input = exports.weak = exports.strong = void 0;
const tslib_1 = require("tslib");
const kleur_1 = tslib_1.__importDefault(require("kleur"));
exports.strong = kleur_1.default.bold;
exports.weak = kleur_1.default.dim;
exports.input = kleur_1.default.cyan;
exports.success = kleur_1.default.green;
exports.failure = kleur_1.default.red;
exports.ancillary = kleur_1.default.cyan;
const COLORS = {
strong: exports.strong,
weak: exports.weak,
input: exports.input,
success: exports.success,
failure: exports.failure,
ancillary: exports.ancillary,
log: {
DEBUG: kleur_1.default.magenta,
INFO: kleur_1.default.cyan,
WARN: kleur_1.default.yellow,
ERROR: kleur_1.default.red,
},
};
exports.default = COLORS;

View file

@ -0,0 +1,398 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
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.wait = exports.checkAppName = exports.checkAppId = exports.checkAppDir = exports.checkAppConfig = exports.checkCapacitorPlatform = exports.checkPackage = exports.checkWebDir = exports.check = void 0;
const tslib_1 = require("tslib");
const utils_fs_1 = require("@ionic/utils-fs");
const utils_terminal_1 = require("@ionic/utils-terminal");
const path_1 = require("path");
const colors_1 = tslib_1.__importDefault(require("./colors"));
const errors_1 = require("./errors");
const log_1 = require("./log");
const monorepotools_1 = require("./util/monorepotools");
const node_1 = require("./util/node");
const subprocess_1 = require("./util/subprocess");
async function check(checks) {
const results = await Promise.all(checks.map(f => f()));
const errors = results.filter(r => r != null);
if (errors.length > 0) {
throw errors.join('\n');
}
}
exports.check = check;
async function checkWebDir(config) {
var _a;
// We can skip checking the web dir if a server URL is set.
if ((_a = config.app.extConfig.server) === null || _a === void 0 ? void 0 : _a.url) {
return null;
}
const invalidFolders = ['', '.', '..', '../', './'];
if (invalidFolders.includes(config.app.webDir)) {
return `"${config.app.webDir}" is not a valid value for webDir`;
}
if (!(await (0, utils_fs_1.pathExists)(config.app.webDirAbs))) {
return (`Could not find the web assets directory: ${colors_1.default.strong((0, utils_terminal_1.prettyPath)(config.app.webDirAbs))}.\n` +
`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')}`);
}
if (!(await (0, utils_fs_1.pathExists)((0, path_1.join)(config.app.webDirAbs, 'index.html')))) {
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` +
`It will be the entry point for the web portion of the Capacitor app.`);
}
return null;
}
exports.checkWebDir = checkWebDir;
async function checkPackage() {
if (!(await (0, utils_fs_1.pathExists)('package.json'))) {
if (await (0, utils_fs_1.pathExists)('project.json')) {
return null;
}
else {
return (`The Capacitor CLI needs to run at the root of an npm package or in a valid NX monorepo.\n` +
`Make sure you have a package.json or project.json file in the directory where you run the Capacitor CLI.\n` +
`More info: ${colors_1.default.strong('https://docs.npmjs.com/cli/init')}`);
}
}
return null;
}
exports.checkPackage = checkPackage;
async function checkCapacitorPlatform(config, platform) {
const pkg = await getCapacitorPackage(config, platform);
if (!pkg) {
return (`Could not find the ${colors_1.default.input(platform)} platform.\n` +
`You must install it in your project first, e.g. w/ ${colors_1.default.input(`npm install @capacitor/${platform}`)}`);
}
return null;
}
exports.checkCapacitorPlatform = checkCapacitorPlatform;
async function checkAppConfig(config) {
if (!config.app.appId) {
return (`Missing ${colors_1.default.input('appId')} for new platform.\n` +
`Please add it in ${config.app.extConfigName} or run ${colors_1.default.input('npx cap init')}.`);
}
if (!config.app.appName) {
return (`Missing ${colors_1.default.input('appName')} for new platform.\n` +
`Please add it in ${config.app.extConfigName} or run ${colors_1.default.input('npx cap init')}.`);
}
const appIdError = await checkAppId(config, config.app.appId);
if (appIdError) {
return appIdError;
}
const appNameError = await checkAppName(config, config.app.appName);
if (appNameError) {
return appNameError;
}
return null;
}
exports.checkAppConfig = checkAppConfig;
async function checkAppDir(config, dir) {
if (!/^\S*$/.test(dir)) {
return `Your app directory should not contain spaces`;
}
return null;
}
exports.checkAppDir = checkAppDir;
async function checkAppId(config, id) {
if (!id) {
return `Invalid App ID. Must be in Java package form with no dashes (ex: com.example.app)`;
}
if (/^[a-z][a-z0-9_]*(\.[a-z0-9_]+)+$/.test(id.toLowerCase())) {
return null;
}
return `Invalid App ID "${id}". Must be in Java package form with no dashes (ex: com.example.app)`;
}
exports.checkAppId = checkAppId;
async function checkAppName(config, name) {
// We allow pretty much anything right now, have fun
if (!(name === null || name === void 0 ? void 0 : name.length)) {
return `Must provide an app name. For example: 'Spacebook'`;
}
return null;
}
exports.checkAppName = checkAppName;
async function wait(time) {
return new Promise(resolve => setTimeout(resolve, time));
}
exports.wait = wait;
async function runPlatformHook(config, platformName, platformDir, hook) {
var _a;
const { spawn } = await Promise.resolve().then(() => tslib_1.__importStar(require('child_process')));
let pkg;
if ((0, monorepotools_1.isNXMonorepo)(platformDir)) {
pkg = await (0, utils_fs_1.readJSON)((0, path_1.join)((0, monorepotools_1.findNXMonorepoRoot)(platformDir), 'package.json'));
}
else {
pkg = await (0, utils_fs_1.readJSON)((0, path_1.join)(platformDir, 'package.json'));
}
const cmd = (_a = pkg.scripts) === null || _a === void 0 ? void 0 : _a[hook];
if (!cmd) {
return;
}
return new Promise((resolve, reject) => {
const p = spawn(cmd, {
stdio: 'inherit',
shell: true,
cwd: platformDir,
env: {
INIT_CWD: platformDir,
CAPACITOR_ROOT_DIR: config.app.rootDir,
CAPACITOR_WEB_DIR: config.app.webDirAbs,
CAPACITOR_CONFIG: JSON.stringify(config.app.extConfig),
CAPACITOR_PLATFORM_NAME: platformName,
...process.env,
},
});
p.on('close', () => {
resolve();
});
p.on('error', err => {
reject(err);
});
});
}
exports.runPlatformHook = runPlatformHook;
async function runTask(title, fn) {
const chain = log_1.output.createTaskChain();
chain.next(title);
try {
const value = await fn();
chain.end();
return value;
}
catch (e) {
chain.fail();
throw e;
}
}
exports.runTask = runTask;
async function getCapacitorPackage(config, name) {
const packagePath = (0, node_1.resolveNode)(config.app.rootDir, `@capacitor/${name}`, 'package.json');
if (!packagePath) {
return null;
}
return (0, utils_fs_1.readJSON)(packagePath);
}
exports.getCapacitorPackage = getCapacitorPackage;
async function requireCapacitorPackage(config, name) {
const pkg = await getCapacitorPackage(config, name);
if (!pkg) {
(0, errors_1.fatal)(`Unable to find node_modules/@capacitor/${name}.\n` +
`Are you sure ${colors_1.default.strong(`@capacitor/${name}`)} is installed?`);
}
return pkg;
}
exports.requireCapacitorPackage = requireCapacitorPackage;
async function getCapacitorPackageVersion(config, platform) {
return (await requireCapacitorPackage(config, platform)).version;
}
exports.getCapacitorPackageVersion = getCapacitorPackageVersion;
async function getCoreVersion(config) {
return getCapacitorPackageVersion(config, 'core');
}
exports.getCoreVersion = getCoreVersion;
async function getCLIVersion(config) {
return getCapacitorPackageVersion(config, 'cli');
}
exports.getCLIVersion = getCLIVersion;
function getPlatformDirectory(config, platform) {
switch (platform) {
case 'android':
return config.android.platformDirAbs;
case 'ios':
return config.ios.platformDirAbs;
case 'web':
return config.web.platformDirAbs;
}
return null;
}
async function getProjectPlatformDirectory(config, platform) {
const platformPath = getPlatformDirectory(config, platform);
if (platformPath && (await (0, utils_fs_1.pathExists)(platformPath))) {
return platformPath;
}
return null;
}
exports.getProjectPlatformDirectory = getProjectPlatformDirectory;
async function selectPlatforms(config, selectedPlatformName) {
if (selectedPlatformName) {
// already passed in a platform name
const platformName = selectedPlatformName.toLowerCase().trim();
if (!(await isValidPlatform(platformName))) {
(0, errors_1.fatal)(`Invalid platform: ${colors_1.default.input(platformName)}`);
}
else if (!(await getProjectPlatformDirectory(config, platformName))) {
if (platformName === 'web') {
(0, errors_1.fatal)(`Could not find the web platform directory.\n` +
`Make sure ${colors_1.default.strong(config.app.webDir)} exists.`);
}
(0, errors_1.fatal)(`${colors_1.default.strong(platformName)} platform has not been added yet.\n` +
`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`)}`);
}
// return the platform in an string array
return [platformName];
}
// wasn't given a platform name, so let's
// get the platforms that have already been created
return getAddedPlatforms(config);
}
exports.selectPlatforms = selectPlatforms;
async function getKnownPlatforms() {
return ['web', 'android', 'ios'];
}
exports.getKnownPlatforms = getKnownPlatforms;
async function isValidPlatform(platform) {
return (await getKnownPlatforms()).includes(platform);
}
exports.isValidPlatform = isValidPlatform;
async function getKnownCommunityPlatforms() {
return ['electron'];
}
exports.getKnownCommunityPlatforms = getKnownCommunityPlatforms;
async function isValidCommunityPlatform(platform) {
return (await getKnownCommunityPlatforms()).includes(platform);
}
exports.isValidCommunityPlatform = isValidCommunityPlatform;
async function getKnownEnterprisePlatforms() {
return ['windows'];
}
exports.getKnownEnterprisePlatforms = getKnownEnterprisePlatforms;
async function isValidEnterprisePlatform(platform) {
return (await getKnownEnterprisePlatforms()).includes(platform);
}
exports.isValidEnterprisePlatform = isValidEnterprisePlatform;
async function promptForPlatform(platforms, promptMessage, selectedPlatformName) {
const { prompt } = await Promise.resolve().then(() => tslib_1.__importStar(require('prompts')));
if (!selectedPlatformName) {
const answers = await prompt([
{
type: 'select',
name: 'mode',
message: promptMessage,
choices: platforms.map(p => ({ title: p, value: p })),
},
], { onCancel: () => process.exit(1) });
return answers.mode.toLowerCase().trim();
}
const platformName = selectedPlatformName.toLowerCase().trim();
if (!(await isValidPlatform(platformName))) {
const knownPlatforms = await getKnownPlatforms();
(0, errors_1.fatal)(`Invalid platform: ${colors_1.default.input(platformName)}.\n` +
`Valid platforms include: ${knownPlatforms.join(', ')}`);
}
return platformName;
}
exports.promptForPlatform = promptForPlatform;
async function promptForPlatformTarget(targets, selectedTarget) {
const { prompt } = await Promise.resolve().then(() => tslib_1.__importStar(require('prompts')));
const validTargets = targets.filter(t => t.id !== undefined);
if (!selectedTarget) {
if (validTargets.length === 1) {
return validTargets[0];
}
else {
const answers = await prompt([
{
type: 'select',
name: 'target',
message: 'Please choose a target device:',
choices: validTargets.map(t => ({
title: `${getPlatformTargetName(t)} (${t.id})`,
value: t,
})),
},
], { onCancel: () => process.exit(1) });
return answers.target;
}
}
const targetID = selectedTarget.trim();
const target = targets.find(t => t.id === targetID);
if (!target) {
(0, errors_1.fatal)(`Invalid target ID: ${colors_1.default.input(targetID)}.\n` +
`Valid targets are: ${targets.map(t => t.id).join(', ')}`);
}
return target;
}
exports.promptForPlatformTarget = promptForPlatformTarget;
function getPlatformTargetName(target) {
var _a, _b, _c;
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
? ` (${target.platform === 'ios' ? 'simulator' : 'emulator'})`
: ''}`;
}
exports.getPlatformTargetName = getPlatformTargetName;
async function getAddedPlatforms(config) {
const platforms = [];
if (await getProjectPlatformDirectory(config, config.android.name)) {
platforms.push(config.android.name);
}
if (await getProjectPlatformDirectory(config, config.ios.name)) {
platforms.push(config.ios.name);
}
platforms.push(config.web.name);
return platforms;
}
exports.getAddedPlatforms = getAddedPlatforms;
async function checkPlatformVersions(config, platform) {
const semver = await Promise.resolve().then(() => tslib_1.__importStar(require('semver')));
const coreVersion = await getCoreVersion(config);
const platformVersion = await getCapacitorPackageVersion(config, platform);
if (semver.diff(coreVersion, platformVersion) === 'minor' ||
semver.diff(coreVersion, platformVersion) === 'major') {
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` +
`Consider updating to a matching version, e.g. w/ ${colors_1.default.input(`npm install @capacitor/core@${platformVersion}`)}`);
}
}
exports.checkPlatformVersions = checkPlatformVersions;
function resolvePlatform(config, platform) {
if (platform[0] !== '@') {
const core = (0, node_1.resolveNode)(config.app.rootDir, `@capacitor/${platform}`, 'package.json');
if (core) {
return (0, path_1.dirname)(core);
}
const community = (0, node_1.resolveNode)(config.app.rootDir, `@capacitor-community/${platform}`, 'package.json');
if (community) {
return (0, path_1.dirname)(community);
}
const enterprise = (0, node_1.resolveNode)(config.app.rootDir, `@ionic-enterprise/capacitor-${platform}`, 'package.json');
if (enterprise) {
return (0, path_1.dirname)(enterprise);
}
}
// third-party
const thirdParty = (0, node_1.resolveNode)(config.app.rootDir, platform, 'package.json');
if (thirdParty) {
return (0, path_1.dirname)(thirdParty);
}
return null;
}
exports.resolvePlatform = resolvePlatform;
async function checkJDKMajorVersion() {
try {
const string = await (0, subprocess_1.runCommand)('java', ['--version']);
const versionRegex = RegExp(/([0-9]+)\.?([0-9]*)\.?([0-9]*)/);
const versionMatch = versionRegex.exec(string);
if (versionMatch === null) {
return -1;
}
const firstVersionNumber = parseInt(versionMatch[1]);
const secondVersionNumber = parseInt(versionMatch[2]);
if (typeof firstVersionNumber === 'number' && firstVersionNumber != 1) {
return firstVersionNumber;
}
else if (typeof secondVersionNumber === 'number' &&
firstVersionNumber == 1 &&
secondVersionNumber < 9) {
return secondVersionNumber;
}
else {
return -1;
}
}
catch (e) {
return -1;
}
}
exports.checkJDKMajorVersion = checkJDKMajorVersion;
function parseApkNameFromFlavor(flavor) {
const convertedName = flavor.replace(/([A-Z])/g, '$1').toLowerCase();
return `app-${convertedName ? `${convertedName}-` : ''}debug.apk`;
}
exports.parseApkNameFromFlavor = parseApkNameFromFlavor;

View file

@ -0,0 +1,396 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.checkExternalConfig = exports.writeConfig = exports.loadConfig = exports.CONFIG_FILE_NAME_JSON = exports.CONFIG_FILE_NAME_JS = exports.CONFIG_FILE_NAME_TS = void 0;
const tslib_1 = require("tslib");
const utils_fs_1 = require("@ionic/utils-fs");
const debug_1 = tslib_1.__importDefault(require("debug"));
const path_1 = require("path");
const colors_1 = tslib_1.__importDefault(require("./colors"));
const common_1 = require("./common");
const errors_1 = require("./errors");
const log_1 = require("./log");
const fn_1 = require("./util/fn");
const js_1 = require("./util/js");
const monorepotools_1 = require("./util/monorepotools");
const node_1 = require("./util/node");
const promise_1 = require("./util/promise");
const subprocess_1 = require("./util/subprocess");
const debug = (0, debug_1.default)('capacitor:config');
exports.CONFIG_FILE_NAME_TS = 'capacitor.config.ts';
exports.CONFIG_FILE_NAME_JS = 'capacitor.config.js';
exports.CONFIG_FILE_NAME_JSON = 'capacitor.config.json';
async function loadConfig() {
var _a, _b, _c, _d;
const appRootDir = process.cwd();
const cliRootDir = (0, path_1.dirname)(__dirname);
const conf = await loadExtConfig(appRootDir);
const depsForNx = await (async () => {
var _a, _b;
if ((0, monorepotools_1.isNXMonorepo)(appRootDir)) {
const rootOfNXMonorepo = (0, monorepotools_1.findNXMonorepoRoot)(appRootDir);
const pkgJSONOfMonorepoRoot = await (0, fn_1.tryFn)(utils_fs_1.readJSON, (0, path_1.resolve)(rootOfNXMonorepo, 'package.json'));
const devDependencies = (_a = pkgJSONOfMonorepoRoot === null || pkgJSONOfMonorepoRoot === void 0 ? void 0 : pkgJSONOfMonorepoRoot.devDependencies) !== null && _a !== void 0 ? _a : {};
const dependencies = (_b = pkgJSONOfMonorepoRoot === null || pkgJSONOfMonorepoRoot === void 0 ? void 0 : pkgJSONOfMonorepoRoot.dependencies) !== null && _b !== void 0 ? _b : {};
return {
devDependencies,
dependencies,
};
}
return {};
})();
const appId = (_a = conf.extConfig.appId) !== null && _a !== void 0 ? _a : '';
const appName = (_b = conf.extConfig.appName) !== null && _b !== void 0 ? _b : '';
const webDir = (_c = conf.extConfig.webDir) !== null && _c !== void 0 ? _c : 'www';
const cli = await loadCLIConfig(cliRootDir);
const config = {
android: await loadAndroidConfig(appRootDir, conf.extConfig, cli),
ios: await loadIOSConfig(appRootDir, conf.extConfig),
web: await loadWebConfig(appRootDir, webDir),
cli,
app: {
rootDir: appRootDir,
appId,
appName,
webDir,
webDirAbs: (0, path_1.resolve)(appRootDir, webDir),
package: (_d = (await (0, fn_1.tryFn)(utils_fs_1.readJSON, (0, path_1.resolve)(appRootDir, 'package.json')))) !== null && _d !== void 0 ? _d : {
name: appName,
version: '1.0.0',
...depsForNx,
},
...conf,
},
};
debug('config: %O', config);
return config;
}
exports.loadConfig = loadConfig;
async function writeConfig(extConfig, extConfigFilePath) {
switch ((0, path_1.extname)(extConfigFilePath)) {
case '.json': {
await (0, utils_fs_1.writeJSON)(extConfigFilePath, extConfig, { spaces: 2 });
break;
}
case '.ts': {
await (0, utils_fs_1.writeFile)(extConfigFilePath, formatConfigTS(extConfig));
break;
}
}
}
exports.writeConfig = writeConfig;
async function loadExtConfigTS(rootDir, extConfigName, extConfigFilePath) {
var _a, _b;
try {
const tsPath = (0, node_1.resolveNode)(rootDir, 'typescript');
if (!tsPath) {
(0, errors_1.fatal)('Could not find installation of TypeScript.\n' +
`To use ${colors_1.default.strong(extConfigName)} files, you must install TypeScript in your project, e.g. w/ ${colors_1.default.input('npm install -D typescript')}`);
}
const ts = require(tsPath); // eslint-disable-line @typescript-eslint/no-var-requires
const extConfigObject = (0, node_1.requireTS)(ts, extConfigFilePath);
const extConfig = (_a = extConfigObject.default) !== null && _a !== void 0 ? _a : extConfigObject;
return {
extConfigType: 'ts',
extConfigName,
extConfigFilePath: extConfigFilePath,
extConfig,
};
}
catch (e) {
if (!(0, errors_1.isFatal)(e)) {
(0, errors_1.fatal)(`Parsing ${colors_1.default.strong(extConfigName)} failed.\n\n${(_b = e.stack) !== null && _b !== void 0 ? _b : e}`);
}
throw e;
}
}
async function loadExtConfigJS(rootDir, extConfigName, extConfigFilePath) {
var _a;
try {
return {
extConfigType: 'js',
extConfigName,
extConfigFilePath: extConfigFilePath,
extConfig: require(extConfigFilePath),
};
}
catch (e) {
(0, errors_1.fatal)(`Parsing ${colors_1.default.strong(extConfigName)} failed.\n\n${(_a = e.stack) !== null && _a !== void 0 ? _a : e}`);
}
}
async function loadExtConfig(rootDir) {
var _a;
const extConfigFilePathTS = (0, path_1.resolve)(rootDir, exports.CONFIG_FILE_NAME_TS);
if (await (0, utils_fs_1.pathExists)(extConfigFilePathTS)) {
return loadExtConfigTS(rootDir, exports.CONFIG_FILE_NAME_TS, extConfigFilePathTS);
}
const extConfigFilePathJS = (0, path_1.resolve)(rootDir, exports.CONFIG_FILE_NAME_JS);
if (await (0, utils_fs_1.pathExists)(extConfigFilePathJS)) {
return loadExtConfigJS(rootDir, exports.CONFIG_FILE_NAME_JS, extConfigFilePathJS);
}
const extConfigFilePath = (0, path_1.resolve)(rootDir, exports.CONFIG_FILE_NAME_JSON);
return {
extConfigType: 'json',
extConfigName: exports.CONFIG_FILE_NAME_JSON,
extConfigFilePath: extConfigFilePath,
extConfig: (_a = (await (0, fn_1.tryFn)(utils_fs_1.readJSON, extConfigFilePath))) !== null && _a !== void 0 ? _a : {},
};
}
async function loadCLIConfig(rootDir) {
const assetsDir = 'assets';
const assetsDirAbs = (0, path_1.join)(rootDir, assetsDir);
const iosPlatformTemplateArchive = 'ios-template.tar.gz';
const iosCordovaPluginsTemplateArchive = 'capacitor-cordova-ios-plugins.tar.gz';
const androidPlatformTemplateArchive = 'android-template.tar.gz';
const androidCordovaPluginsTemplateArchive = 'capacitor-cordova-android-plugins.tar.gz';
return {
rootDir,
assetsDir,
assetsDirAbs,
assets: {
ios: {
platformTemplateArchive: iosPlatformTemplateArchive,
platformTemplateArchiveAbs: (0, path_1.resolve)(assetsDirAbs, iosPlatformTemplateArchive),
cordovaPluginsTemplateArchive: iosCordovaPluginsTemplateArchive,
cordovaPluginsTemplateArchiveAbs: (0, path_1.resolve)(assetsDirAbs, iosCordovaPluginsTemplateArchive),
},
android: {
platformTemplateArchive: androidPlatformTemplateArchive,
platformTemplateArchiveAbs: (0, path_1.resolve)(assetsDirAbs, androidPlatformTemplateArchive),
cordovaPluginsTemplateArchive: androidCordovaPluginsTemplateArchive,
cordovaPluginsTemplateArchiveAbs: (0, path_1.resolve)(assetsDirAbs, androidCordovaPluginsTemplateArchive),
},
},
package: await (0, utils_fs_1.readJSON)((0, path_1.resolve)(rootDir, 'package.json')),
os: determineOS(process.platform),
};
}
async function loadAndroidConfig(rootDir, extConfig, cliConfig) {
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s;
const name = 'android';
const platformDir = (_b = (_a = extConfig.android) === null || _a === void 0 ? void 0 : _a.path) !== null && _b !== void 0 ? _b : 'android';
const platformDirAbs = (0, path_1.resolve)(rootDir, platformDir);
const appDir = 'app';
const srcDir = `${appDir}/src`;
const srcMainDir = `${srcDir}/main`;
const assetsDir = `${srcMainDir}/assets`;
const webDir = `${assetsDir}/public`;
const resDir = `${srcMainDir}/res`;
let apkPath = `${appDir}/build/outputs/apk/`;
const flavor = ((_c = extConfig.android) === null || _c === void 0 ? void 0 : _c.flavor) || '';
if ((_d = extConfig.android) === null || _d === void 0 ? void 0 : _d.flavor) {
apkPath = `${apkPath}/${(_e = extConfig.android) === null || _e === void 0 ? void 0 : _e.flavor}`;
}
const apkName = (0, common_1.parseApkNameFromFlavor)(flavor);
const buildOutputDir = `${apkPath}/debug`;
const cordovaPluginsDir = 'capacitor-cordova-android-plugins';
const studioPath = (0, promise_1.lazy)(() => determineAndroidStudioPath(cliConfig.os));
const buildOptions = {
keystorePath: (_g = (_f = extConfig.android) === null || _f === void 0 ? void 0 : _f.buildOptions) === null || _g === void 0 ? void 0 : _g.keystorePath,
keystorePassword: (_j = (_h = extConfig.android) === null || _h === void 0 ? void 0 : _h.buildOptions) === null || _j === void 0 ? void 0 : _j.keystorePassword,
keystoreAlias: (_l = (_k = extConfig.android) === null || _k === void 0 ? void 0 : _k.buildOptions) === null || _l === void 0 ? void 0 : _l.keystoreAlias,
keystoreAliasPassword: (_o = (_m = extConfig.android) === null || _m === void 0 ? void 0 : _m.buildOptions) === null || _o === void 0 ? void 0 : _o.keystoreAliasPassword,
signingType: (_q = (_p = extConfig.android) === null || _p === void 0 ? void 0 : _p.buildOptions) === null || _q === void 0 ? void 0 : _q.signingType,
releaseType: (_s = (_r = extConfig.android) === null || _r === void 0 ? void 0 : _r.buildOptions) === null || _s === void 0 ? void 0 : _s.releaseType,
};
return {
name,
minVersion: '22',
studioPath,
platformDir,
platformDirAbs,
cordovaPluginsDir,
cordovaPluginsDirAbs: (0, path_1.resolve)(platformDirAbs, cordovaPluginsDir),
appDir,
appDirAbs: (0, path_1.resolve)(platformDirAbs, appDir),
srcDir,
srcDirAbs: (0, path_1.resolve)(platformDirAbs, srcDir),
srcMainDir,
srcMainDirAbs: (0, path_1.resolve)(platformDirAbs, srcMainDir),
assetsDir,
assetsDirAbs: (0, path_1.resolve)(platformDirAbs, assetsDir),
webDir,
webDirAbs: (0, path_1.resolve)(platformDirAbs, webDir),
resDir,
resDirAbs: (0, path_1.resolve)(platformDirAbs, resDir),
apkName,
buildOutputDir,
buildOutputDirAbs: (0, path_1.resolve)(platformDirAbs, buildOutputDir),
flavor,
buildOptions,
};
}
async function loadIOSConfig(rootDir, extConfig) {
var _a, _b, _c, _d;
const name = 'ios';
const platformDir = (_b = (_a = extConfig.ios) === null || _a === void 0 ? void 0 : _a.path) !== null && _b !== void 0 ? _b : 'ios';
const platformDirAbs = (0, path_1.resolve)(rootDir, platformDir);
const scheme = (_d = (_c = extConfig.ios) === null || _c === void 0 ? void 0 : _c.scheme) !== null && _d !== void 0 ? _d : 'App';
const nativeProjectDir = 'App';
const nativeProjectDirAbs = (0, path_1.resolve)(platformDirAbs, nativeProjectDir);
const nativeTargetDir = `${nativeProjectDir}/App`;
const nativeTargetDirAbs = (0, path_1.resolve)(platformDirAbs, nativeTargetDir);
const nativeXcodeProjDir = `${nativeProjectDir}/App.xcodeproj`;
const nativeXcodeProjDirAbs = (0, path_1.resolve)(platformDirAbs, nativeXcodeProjDir);
const nativeXcodeWorkspaceDirAbs = (0, promise_1.lazy)(() => determineXcodeWorkspaceDirAbs(nativeProjectDirAbs));
const podPath = (0, promise_1.lazy)(() => determineGemfileOrCocoapodPath(rootDir, platformDirAbs, nativeProjectDirAbs));
const webDirAbs = (0, promise_1.lazy)(() => determineIOSWebDirAbs(nativeProjectDirAbs, nativeTargetDirAbs, nativeXcodeProjDirAbs));
const cordovaPluginsDir = 'capacitor-cordova-ios-plugins';
return {
name,
minVersion: '13.0',
platformDir,
platformDirAbs,
scheme,
cordovaPluginsDir,
cordovaPluginsDirAbs: (0, path_1.resolve)(platformDirAbs, cordovaPluginsDir),
nativeProjectDir,
nativeProjectDirAbs,
nativeTargetDir,
nativeTargetDirAbs,
nativeXcodeProjDir,
nativeXcodeProjDirAbs,
nativeXcodeWorkspaceDir: (0, promise_1.lazy)(async () => (0, path_1.relative)(platformDirAbs, await nativeXcodeWorkspaceDirAbs)),
nativeXcodeWorkspaceDirAbs,
webDir: (0, promise_1.lazy)(async () => (0, path_1.relative)(platformDirAbs, await webDirAbs)),
webDirAbs,
podPath,
};
}
async function loadWebConfig(rootDir, webDir) {
const platformDir = webDir;
const platformDirAbs = (0, path_1.resolve)(rootDir, platformDir);
return {
name: 'web',
platformDir,
platformDirAbs,
};
}
function determineOS(os) {
switch (os) {
case 'darwin':
return "mac" /* OS.Mac */;
case 'win32':
return "windows" /* OS.Windows */;
case 'linux':
return "linux" /* OS.Linux */;
}
return "unknown" /* OS.Unknown */;
}
async function determineXcodeWorkspaceDirAbs(nativeProjectDirAbs) {
return (0, path_1.resolve)(nativeProjectDirAbs, 'App.xcworkspace');
}
async function determineIOSWebDirAbs(nativeProjectDirAbs, nativeTargetDirAbs, nativeXcodeProjDirAbs) {
const re = /path\s=\spublic[\s\S]+?sourceTree\s=\s([^;]+)/;
const pbxprojPath = (0, path_1.resolve)(nativeXcodeProjDirAbs, 'project.pbxproj');
try {
const pbxproj = await (0, utils_fs_1.readFile)(pbxprojPath, { encoding: 'utf8' });
const m = pbxproj.match(re);
if (m && m[1] === 'SOURCE_ROOT') {
log_1.logger.warn(`Using the iOS project root for the ${colors_1.default.strong('public')} directory is deprecated.\n` +
`Please follow the Upgrade Guide to move ${colors_1.default.strong('public')} inside the iOS target directory: ${colors_1.default.strong('https://capacitorjs.com/docs/updating/3-0#move-public-into-the-ios-target-directory')}`);
return (0, path_1.resolve)(nativeProjectDirAbs, 'public');
}
}
catch (e) {
// ignore
}
return (0, path_1.resolve)(nativeTargetDirAbs, 'public');
}
async function determineAndroidStudioPath(os) {
if (process.env.CAPACITOR_ANDROID_STUDIO_PATH) {
return process.env.CAPACITOR_ANDROID_STUDIO_PATH;
}
switch (os) {
case "mac" /* OS.Mac */:
return '/Applications/Android Studio.app';
case "windows" /* OS.Windows */: {
const { runCommand } = await Promise.resolve().then(() => tslib_1.__importStar(require('./util/subprocess')));
let p = 'C:\\Program Files\\Android\\Android Studio\\bin\\studio64.exe';
try {
if (!(await (0, utils_fs_1.pathExists)(p))) {
let commandResult = await runCommand('REG', [
'QUERY',
'HKEY_LOCAL_MACHINE\\SOFTWARE\\Android Studio',
'/v',
'Path',
]);
commandResult = commandResult.replace(/(\r\n|\n|\r)/gm, '');
const i = commandResult.indexOf('REG_SZ');
if (i > 0) {
p = commandResult.substring(i + 6).trim() + '\\bin\\studio64.exe';
}
}
}
catch (e) {
debug(`Error checking registry for Android Studio path: %O`, e);
break;
}
return p;
}
case "linux" /* OS.Linux */:
return '/usr/local/android-studio/bin/studio.sh';
}
return '';
}
async function determineGemfileOrCocoapodPath(rootDir, platformDir, nativeProjectDirAbs) {
if (process.env.CAPACITOR_COCOAPODS_PATH) {
return process.env.CAPACITOR_COCOAPODS_PATH;
}
let gemfilePath = '';
if (await (0, utils_fs_1.pathExists)((0, path_1.resolve)(rootDir, 'Gemfile'))) {
gemfilePath = (0, path_1.resolve)(rootDir, 'Gemfile');
}
else if (await (0, utils_fs_1.pathExists)((0, path_1.resolve)(platformDir, 'Gemfile'))) {
gemfilePath = (0, path_1.resolve)(platformDir, 'Gemfile');
}
else if (await (0, utils_fs_1.pathExists)((0, path_1.resolve)(nativeProjectDirAbs, 'Gemfile'))) {
gemfilePath = (0, path_1.resolve)(nativeProjectDirAbs, 'Gemfile');
}
const appSpecificGemfileExists = gemfilePath != '';
// Multi-app projects might share a single global 'Gemfile' at the Git repository root directory.
if (!appSpecificGemfileExists) {
try {
const output = await (0, subprocess_1.getCommandOutput)('git', ['rev-parse', '--show-toplevel'], { cwd: rootDir });
if (output != null) {
gemfilePath = (0, path_1.resolve)(output, 'Gemfile');
}
}
catch (e) {
// Nothing
}
}
try {
const gemfileText = (await (0, utils_fs_1.readFile)(gemfilePath)).toString();
if (!gemfileText) {
return 'pod';
}
const cocoapodsInGemfile = new RegExp(/gem 'cocoapods'/).test(gemfileText);
if (cocoapodsInGemfile) {
return 'bundle exec pod';
}
else {
return 'pod';
}
}
catch {
return 'pod';
}
}
function formatConfigTS(extConfig) {
// TODO: <reference> tags
return `import { CapacitorConfig } from '@capacitor/cli';
const config: CapacitorConfig = ${(0, js_1.formatJSObject)(extConfig)};
export default config;\n`;
}
function checkExternalConfig(config) {
if (typeof config.extConfig.bundledWebRuntime !== 'undefined') {
let actionMessage = `Can be safely deleted.`;
if (config.extConfig.bundledWebRuntime === true) {
actionMessage = `Please, use a bundler to bundle Capacitor and its plugins.`;
}
log_1.logger.warn(`The ${colors_1.default.strong('bundledWebRuntime')} configuration option has been deprecated. ${actionMessage}`);
}
}
exports.checkExternalConfig = checkExternalConfig;

View file

@ -0,0 +1,829 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.writeCordovaAndroidManifest = exports.getCordovaPreferences = exports.needsStaticPod = exports.getIncompatibleCordovaPlugins = exports.checkPluginDependencies = exports.logCordovaManualSteps = exports.getCordovaPlugins = exports.handleCordovaPluginsJS = exports.autoGenerateConfig = exports.removePluginFiles = exports.createEmptyCordovaJS = exports.copyCordovaJS = exports.copyPluginsJS = exports.generateCordovaPluginsJSFile = void 0;
const tslib_1 = require("tslib");
const utils_fs_1 = require("@ionic/utils-fs");
const path_1 = require("path");
const plist_1 = tslib_1.__importDefault(require("plist"));
const prompts_1 = tslib_1.__importDefault(require("prompts"));
const common_1 = require("./android/common");
const colors_1 = tslib_1.__importDefault(require("./colors"));
const errors_1 = require("./errors");
const common_2 = require("./ios/common");
const log_1 = require("./log");
const plugin_1 = require("./plugin");
const node_1 = require("./util/node");
const term_1 = require("./util/term");
const xml_1 = require("./util/xml");
/**
* Build the root cordova_plugins.js file referencing each Plugin JS file.
*/
function generateCordovaPluginsJSFile(config, plugins, platform) {
const pluginModules = [];
const pluginExports = [];
plugins.map(p => {
const pluginId = p.xml.$.id;
const jsModules = (0, plugin_1.getJSModules)(p, platform);
jsModules.map((jsModule) => {
const clobbers = [];
const merges = [];
let clobbersModule = '';
let mergesModule = '';
let runsModule = '';
let clobberKey = '';
let mergeKey = '';
if (jsModule.clobbers) {
jsModule.clobbers.map((clobber) => {
clobbers.push(clobber.$.target);
clobberKey = clobber.$.target;
});
clobbersModule = `,
"clobbers": [
"${clobbers.join('",\n "')}"
]`;
}
if (jsModule.merges) {
jsModule.merges.map((merge) => {
merges.push(merge.$.target);
mergeKey = merge.$.target;
});
mergesModule = `,
"merges": [
"${merges.join('",\n "')}"
]`;
}
if (jsModule.runs) {
runsModule = ',\n "runs": true';
}
const pluginModule = {
clobber: clobberKey,
merge: mergeKey,
// mimics Cordova's module name logic if the name attr is missing
pluginContent: `{
"id": "${pluginId +
'.' +
(jsModule.$.name || jsModule.$.src.match(/([^/]+)\.js/)[1])}",
"file": "plugins/${pluginId}/${jsModule.$.src}",
"pluginId": "${pluginId}"${clobbersModule}${mergesModule}${runsModule}
}`,
};
pluginModules.push(pluginModule);
});
pluginExports.push(`"${pluginId}": "${p.xml.$.version}"`);
});
return `
cordova.define('cordova/plugin_list', function(require, exports, module) {
module.exports = [
${pluginModules
.sort((a, b) => a.clobber && b.clobber // Clobbers in alpha order
? a.clobber.localeCompare(b.clobber)
: a.clobber || b.clobber // Clobbers before anything else
? b.clobber.localeCompare(a.clobber)
: a.merge.localeCompare(b.merge))
.map(e => e.pluginContent)
.join(',\n ')}
];
module.exports.metadata =
// TOP OF METADATA
{
${pluginExports.join(',\n ')}
};
// BOTTOM OF METADATA
});
`;
}
exports.generateCordovaPluginsJSFile = generateCordovaPluginsJSFile;
/**
* Build the plugins/* files for each Cordova plugin installed.
*/
async function copyPluginsJS(config, cordovaPlugins, platform) {
const webDir = await getWebDir(config, platform);
const pluginsDir = (0, path_1.join)(webDir, 'plugins');
const cordovaPluginsJSFile = (0, path_1.join)(webDir, 'cordova_plugins.js');
await removePluginFiles(config, platform);
await Promise.all(cordovaPlugins.map(async (p) => {
const pluginId = p.xml.$.id;
const pluginDir = (0, path_1.join)(pluginsDir, pluginId, 'www');
await (0, utils_fs_1.ensureDir)(pluginDir);
const jsModules = (0, plugin_1.getJSModules)(p, platform);
await Promise.all(jsModules.map(async (jsModule) => {
const filePath = (0, path_1.join)(webDir, 'plugins', pluginId, jsModule.$.src);
await (0, utils_fs_1.copy)((0, path_1.join)(p.rootPath, jsModule.$.src), filePath);
let data = await (0, utils_fs_1.readFile)(filePath, { encoding: 'utf-8' });
data = data.trim();
// mimics Cordova's module name logic if the name attr is missing
const name = pluginId +
'.' +
(jsModule.$.name ||
(0, path_1.basename)(jsModule.$.src, (0, path_1.extname)(jsModule.$.src)));
data = `cordova.define("${name}", function(require, exports, module) { \n${data}\n});`;
data = data.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script\s*>/gi, '');
await (0, utils_fs_1.writeFile)(filePath, data, { encoding: 'utf-8' });
}));
const assets = (0, plugin_1.getAssets)(p, platform);
await Promise.all(assets.map(async (asset) => {
const filePath = (0, path_1.join)(webDir, asset.$.target);
await (0, utils_fs_1.copy)((0, path_1.join)(p.rootPath, asset.$.src), filePath);
}));
}));
await (0, utils_fs_1.writeFile)(cordovaPluginsJSFile, generateCordovaPluginsJSFile(config, cordovaPlugins, platform));
}
exports.copyPluginsJS = copyPluginsJS;
async function copyCordovaJS(config, platform) {
const cordovaPath = (0, node_1.resolveNode)(config.app.rootDir, '@capacitor/core', 'cordova.js');
if (!cordovaPath) {
(0, errors_1.fatal)(`Unable to find ${colors_1.default.strong('node_modules/@capacitor/core/cordova.js')}.\n` + `Are you sure ${colors_1.default.strong('@capacitor/core')} is installed?`);
}
return (0, utils_fs_1.copy)(cordovaPath, (0, path_1.join)(await getWebDir(config, platform), 'cordova.js'));
}
exports.copyCordovaJS = copyCordovaJS;
async function createEmptyCordovaJS(config, platform) {
const webDir = await getWebDir(config, platform);
await (0, utils_fs_1.writeFile)((0, path_1.join)(webDir, 'cordova.js'), '');
await (0, utils_fs_1.writeFile)((0, path_1.join)(webDir, 'cordova_plugins.js'), '');
}
exports.createEmptyCordovaJS = createEmptyCordovaJS;
async function removePluginFiles(config, platform) {
const webDir = await getWebDir(config, platform);
const pluginsDir = (0, path_1.join)(webDir, 'plugins');
const cordovaPluginsJSFile = (0, path_1.join)(webDir, 'cordova_plugins.js');
await (0, utils_fs_1.remove)(pluginsDir);
await (0, utils_fs_1.remove)(cordovaPluginsJSFile);
}
exports.removePluginFiles = removePluginFiles;
async function autoGenerateConfig(config, cordovaPlugins, platform) {
var _a, _b, _c, _d;
let xmlDir = (0, path_1.join)(config.android.resDirAbs, 'xml');
const fileName = 'config.xml';
if (platform === 'ios') {
xmlDir = config.ios.nativeTargetDirAbs;
}
await (0, utils_fs_1.ensureDir)(xmlDir);
const cordovaConfigXMLFile = (0, path_1.join)(xmlDir, fileName);
await (0, utils_fs_1.remove)(cordovaConfigXMLFile);
const pluginEntries = [];
cordovaPlugins.map(p => {
const currentPlatform = (0, plugin_1.getPluginPlatform)(p, platform);
if (currentPlatform) {
const configFiles = currentPlatform['config-file'];
if (configFiles) {
const configXMLEntries = configFiles.filter(function (item) {
var _a;
return (_a = item.$) === null || _a === void 0 ? void 0 : _a.target.includes(fileName);
});
configXMLEntries.map((entry) => {
if (entry.feature) {
const feature = { feature: entry.feature };
pluginEntries.push(feature);
}
});
}
}
});
let accessOriginString = [];
if ((_b = (_a = config.app.extConfig) === null || _a === void 0 ? void 0 : _a.cordova) === null || _b === void 0 ? void 0 : _b.accessOrigins) {
accessOriginString = await Promise.all(config.app.extConfig.cordova.accessOrigins.map(async (host) => {
return `
<access origin="${host}" />`;
}));
}
else {
accessOriginString.push(`<access origin="*" />`);
}
const pluginEntriesString = await Promise.all(pluginEntries.map(async (item) => {
const xmlString = await (0, xml_1.writeXML)(item);
return xmlString;
}));
let pluginPreferencesString = [];
if ((_d = (_c = config.app.extConfig) === null || _c === void 0 ? void 0 : _c.cordova) === null || _d === void 0 ? void 0 : _d.preferences) {
pluginPreferencesString = await Promise.all(Object.entries(config.app.extConfig.cordova.preferences).map(async ([key, value]) => {
return `
<preference name="${key}" value="${value}" />`;
}));
}
const content = `<?xml version='1.0' encoding='utf-8'?>
<widget version="1.0.0" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
${accessOriginString.join('')}
${pluginEntriesString.join('')}
${pluginPreferencesString.join('')}
</widget>`;
await (0, utils_fs_1.writeFile)(cordovaConfigXMLFile, content);
}
exports.autoGenerateConfig = autoGenerateConfig;
async function getWebDir(config, platform) {
if (platform === 'ios') {
return config.ios.webDirAbs;
}
if (platform === 'android') {
return config.android.webDirAbs;
}
return '';
}
async function handleCordovaPluginsJS(cordovaPlugins, config, platform) {
const webDir = await getWebDir(config, platform);
await (0, utils_fs_1.mkdirp)(webDir);
if (cordovaPlugins.length > 0) {
(0, plugin_1.printPlugins)(cordovaPlugins, platform, 'cordova');
await copyCordovaJS(config, platform);
await copyPluginsJS(config, cordovaPlugins, platform);
}
else {
await removePluginFiles(config, platform);
await createEmptyCordovaJS(config, platform);
}
await autoGenerateConfig(config, cordovaPlugins, platform);
}
exports.handleCordovaPluginsJS = handleCordovaPluginsJS;
async function getCordovaPlugins(config, platform) {
const allPlugins = await (0, plugin_1.getPlugins)(config, platform);
let plugins = [];
if (platform === config.ios.name) {
plugins = await (0, common_2.getIOSPlugins)(allPlugins);
}
else if (platform === config.android.name) {
plugins = await (0, common_1.getAndroidPlugins)(allPlugins);
}
return plugins.filter(p => (0, plugin_1.getPluginType)(p, platform) === 1 /* PluginType.Cordova */);
}
exports.getCordovaPlugins = getCordovaPlugins;
async function logCordovaManualSteps(cordovaPlugins, config, platform) {
cordovaPlugins.map(p => {
const editConfig = (0, plugin_1.getPlatformElement)(p, platform, 'edit-config');
const configFile = (0, plugin_1.getPlatformElement)(p, platform, 'config-file');
editConfig.concat(configFile).map(async (configElement) => {
if (configElement.$ && !configElement.$.target.includes('config.xml')) {
if (platform === config.ios.name) {
if (configElement.$.target.includes('Info.plist')) {
logiOSPlist(configElement, config, p);
}
}
}
});
});
}
exports.logCordovaManualSteps = logCordovaManualSteps;
async function logiOSPlist(configElement, config, plugin) {
var _a, _b;
let plistPath = (0, path_1.resolve)(config.ios.nativeTargetDirAbs, 'Info.plist');
if ((_a = config.app.extConfig.ios) === null || _a === void 0 ? void 0 : _a.scheme) {
plistPath = (0, path_1.resolve)(config.ios.nativeProjectDirAbs, `${(_b = config.app.extConfig.ios) === null || _b === void 0 ? void 0 : _b.scheme}-Info.plist`);
}
if (!(await (0, utils_fs_1.pathExists)(plistPath))) {
plistPath = (0, path_1.resolve)(config.ios.nativeTargetDirAbs, 'Base.lproj', 'Info.plist');
}
if (await (0, utils_fs_1.pathExists)(plistPath)) {
const xmlMeta = await (0, xml_1.readXML)(plistPath);
const data = await (0, utils_fs_1.readFile)(plistPath, { encoding: 'utf-8' });
const trimmedPlistData = data.replace(/(\t|\r|\n)/g, '');
const plistData = plist_1.default.parse(data);
const dict = xmlMeta.plist.dict.pop();
if (!dict.key.includes(configElement.$.parent)) {
let xml = buildConfigFileXml(configElement);
xml = `<key>${configElement.$.parent}</key>${getConfigFileTagContent(xml)}`;
log_1.logger.warn(`Configuration required for ${colors_1.default.strong(plugin.id)}.\n` +
`Add the following to Info.plist:\n` +
xml);
}
else if (configElement.array || configElement.dict) {
if (configElement.array &&
configElement.array.length > 0 &&
configElement.array[0].string) {
let xml = '';
configElement.array[0].string.map((element) => {
const d = plistData[configElement.$.parent];
if (Array.isArray(d) && !d.includes(element)) {
xml = xml.concat(`<string>${element}</string>\n`);
}
});
if (xml.length > 0) {
log_1.logger.warn(`Configuration required for ${colors_1.default.strong(plugin.id)}.\n` +
`Add the following in the existing ${colors_1.default.strong(configElement.$.parent)} array of your Info.plist:\n` +
xml);
}
}
else {
let xml = buildConfigFileXml(configElement);
xml = `<key>${configElement.$.parent}</key>${getConfigFileTagContent(xml)}`;
xml = `<plist version="1.0"><dict>${xml}</dict></plist>`;
const parseXmlToSearchable = (childElementsObj, arrayToAddTo) => {
for (const childElement of childElementsObj) {
const childElementName = childElement['#name'];
const toAdd = { name: childElementName };
if (childElementName === 'key' || childElementName === 'string') {
toAdd.value = childElement['_'];
}
else {
if (childElement['$']) {
toAdd.attrs = { ...childElement['$'] };
}
if (childElement['$$']) {
toAdd.children = [];
parseXmlToSearchable(childElement['$$'], toAdd['children']);
}
}
arrayToAddTo.push(toAdd);
}
};
const existingElements = (0, xml_1.parseXML)(trimmedPlistData, {
explicitChildren: true,
trim: true,
preserveChildrenOrder: true,
});
const parsedExistingElements = [];
const rootKeyOfExistingElements = Object.keys(existingElements)[0];
const rootOfExistingElementsToAdd = { name: rootKeyOfExistingElements, children: [] };
if (existingElements[rootKeyOfExistingElements]['$']) {
rootOfExistingElementsToAdd.attrs = {
...existingElements[rootKeyOfExistingElements]['$'],
};
}
parseXmlToSearchable(existingElements[rootKeyOfExistingElements]['$$'], rootOfExistingElementsToAdd['children']);
parsedExistingElements.push(rootOfExistingElementsToAdd);
const requiredElements = (0, xml_1.parseXML)(xml, {
explicitChildren: true,
trim: true,
preserveChildrenOrder: true,
});
const parsedRequiredElements = [];
const rootKeyOfRequiredElements = Object.keys(requiredElements)[0];
const rootOfRequiredElementsToAdd = { name: rootKeyOfRequiredElements, children: [] };
if (requiredElements[rootKeyOfRequiredElements]['$']) {
rootOfRequiredElementsToAdd.attrs = {
...requiredElements[rootKeyOfRequiredElements]['$'],
};
}
parseXmlToSearchable(requiredElements[rootKeyOfRequiredElements]['$$'], rootOfRequiredElementsToAdd['children']);
parsedRequiredElements.push(rootOfRequiredElementsToAdd);
const doesContainElements = (requiredElementsArray, existingElementsArray) => {
for (const requiredElement of requiredElementsArray) {
if (requiredElement.name === 'key' ||
requiredElement.name === 'string') {
let foundMatch = false;
for (const existingElement of existingElementsArray) {
if (existingElement.name === requiredElement.name &&
(existingElement.value === requiredElement.value ||
/^[$].{1,}$/.test(requiredElement.value.trim()))) {
foundMatch = true;
break;
}
}
if (!foundMatch) {
return false;
}
}
else {
let foundMatch = false;
for (const existingElement of existingElementsArray) {
if (existingElement.name === requiredElement.name) {
if ((requiredElement.children !== undefined) ===
(existingElement.children !== undefined)) {
if (doesContainElements(requiredElement.children, existingElement.children)) {
foundMatch = true;
break;
}
}
}
}
if (!foundMatch) {
return false;
}
}
}
return true;
};
if (!doesContainElements(parsedRequiredElements, parsedExistingElements)) {
logPossibleMissingItem(configElement, plugin);
}
}
}
}
else {
logPossibleMissingItem(configElement, plugin);
}
}
function logPossibleMissingItem(configElement, plugin) {
let xml = buildConfigFileXml(configElement);
xml = getConfigFileTagContent(xml);
xml = removeOuterTags(xml);
log_1.logger.warn(`Configuration might be missing for ${colors_1.default.strong(plugin.id)}.\n` +
`Add the following to the existing ${colors_1.default.strong(configElement.$.parent)} entry of Info.plist:\n` +
xml);
}
function buildConfigFileXml(configElement) {
return (0, xml_1.buildXmlElement)(configElement, 'config-file');
}
function getConfigFileTagContent(str) {
return str.replace(/<config-file.+">|<\/config-file>/g, '');
}
function removeOuterTags(str) {
const start = str.indexOf('>') + 1;
const end = str.lastIndexOf('<');
return str.substring(start, end);
}
async function checkPluginDependencies(plugins, platform) {
const pluginDeps = new Map();
const cordovaPlugins = plugins.filter(p => (0, plugin_1.getPluginType)(p, platform) === 1 /* PluginType.Cordova */);
const incompatible = plugins.filter(p => (0, plugin_1.getPluginType)(p, platform) === 2 /* PluginType.Incompatible */);
await Promise.all(cordovaPlugins.map(async (p) => {
let allDependencies = [];
allDependencies = allDependencies.concat((0, plugin_1.getPlatformElement)(p, platform, 'dependency'));
if (p.xml['dependency']) {
allDependencies = allDependencies.concat(p.xml['dependency']);
}
allDependencies = allDependencies.filter((dep) => !getIncompatibleCordovaPlugins(platform).includes(dep.$.id) &&
incompatible.filter(p => p.id === dep.$.id || p.xml.$.id === dep.$.id)
.length === 0);
if (allDependencies) {
await Promise.all(allDependencies.map(async (dep) => {
var _a;
let plugin = dep.$.id;
let version = dep.$.version;
if (plugin.includes('@') && plugin.indexOf('@') !== 0) {
[plugin, version] = plugin.split('@');
}
if (cordovaPlugins.filter(p => p.id === plugin || p.xml.$.id === plugin).length === 0) {
if ((_a = dep.$.url) === null || _a === void 0 ? void 0 : _a.startsWith('http')) {
plugin = dep.$.url;
version = dep.$.commit;
}
const deps = pluginDeps.get(p.id) || [];
deps.push(`${plugin}${version ? colors_1.default.weak(` (${version})`) : ''}`);
pluginDeps.set(p.id, deps);
}
}));
}
}));
if (pluginDeps.size > 0) {
let msg = `${colors_1.default.failure(colors_1.default.strong('Plugins are missing dependencies.'))}\n` +
`Cordova plugin dependencies must be installed in your project (e.g. w/ ${colors_1.default.input('npm install')}).\n`;
for (const [plugin, deps] of pluginDeps.entries()) {
msg +=
`\n ${colors_1.default.strong(plugin)} is missing dependencies:\n` +
deps.map(d => ` - ${d}`).join('\n');
}
log_1.logger.warn(`${msg}\n`);
}
}
exports.checkPluginDependencies = checkPluginDependencies;
function getIncompatibleCordovaPlugins(platform) {
const pluginList = [
'cordova-plugin-splashscreen',
'cordova-plugin-ionic-webview',
'cordova-plugin-crosswalk-webview',
'cordova-plugin-wkwebview-engine',
'cordova-plugin-console',
'cordova-plugin-music-controls',
'cordova-plugin-add-swift-support',
'cordova-plugin-ionic-keyboard',
'cordova-plugin-braintree',
'@ionic-enterprise/filesystem',
'@ionic-enterprise/keyboard',
'@ionic-enterprise/splashscreen',
'cordova-support-google-services',
];
if (platform === 'ios') {
pluginList.push('cordova-plugin-statusbar', '@ionic-enterprise/statusbar', 'SalesforceMobileSDK-CordovaPlugin');
}
if (platform === 'android') {
pluginList.push('cordova-plugin-compat');
}
return pluginList;
}
exports.getIncompatibleCordovaPlugins = getIncompatibleCordovaPlugins;
function needsStaticPod(plugin, config) {
var _a, _b, _c, _d;
let pluginList = [
'phonegap-plugin-push',
'@batch.com/cordova-plugin',
'onesignal-cordova-plugin',
];
if ((_b = (_a = config.app.extConfig) === null || _a === void 0 ? void 0 : _a.cordova) === null || _b === void 0 ? void 0 : _b.staticPlugins) {
pluginList = pluginList.concat((_d = (_c = config.app.extConfig) === null || _c === void 0 ? void 0 : _c.cordova) === null || _d === void 0 ? void 0 : _d.staticPlugins);
}
return pluginList.includes(plugin.id) || useFrameworks(plugin);
}
exports.needsStaticPod = needsStaticPod;
function useFrameworks(plugin) {
const podspecs = (0, plugin_1.getPlatformElement)(plugin, 'ios', 'podspec');
const frameworkPods = podspecs.filter((podspec) => podspec.pods.filter((pods) => pods.$ && pods.$['use-frameworks'] === 'true').length > 0);
return frameworkPods.length > 0;
}
async function getCordovaPreferences(config) {
var _a, _b, _c, _d, _e;
const configXml = (0, path_1.join)(config.app.rootDir, 'config.xml');
let cordova = {};
if (await (0, utils_fs_1.pathExists)(configXml)) {
cordova.preferences = {};
const xmlMeta = await (0, xml_1.readXML)(configXml);
if (xmlMeta.widget.preference) {
xmlMeta.widget.preference.map((pref) => {
cordova.preferences[pref.$.name] = pref.$.value;
});
}
}
if (cordova.preferences && Object.keys(cordova.preferences).length > 0) {
if ((0, term_1.isInteractive)()) {
const answers = await (0, log_1.logPrompt)(`${colors_1.default.strong(`Cordova preferences can be automatically ported to ${colors_1.default.strong(config.app.extConfigName)}.`)}\n` +
`Keep in mind: Not all values can be automatically migrated from ${colors_1.default.strong('config.xml')}. There may be more work to do.\n` +
`More info: ${colors_1.default.strong('https://capacitorjs.com/docs/cordova/migrating-from-cordova-to-capacitor')}`, {
type: 'confirm',
name: 'confirm',
message: `Migrate Cordova preferences from config.xml?`,
initial: true,
});
if (answers.confirm) {
if ((_b = (_a = config.app.extConfig) === null || _a === void 0 ? void 0 : _a.cordova) === null || _b === void 0 ? void 0 : _b.preferences) {
const answers = await (0, prompts_1.default)([
{
type: 'confirm',
name: 'confirm',
message: `${config.app.extConfigName} already contains Cordova preferences. Overwrite?`,
},
], { onCancel: () => process.exit(1) });
if (!answers.confirm) {
cordova = (_c = config.app.extConfig) === null || _c === void 0 ? void 0 : _c.cordova;
}
}
}
else {
cordova = (_d = config.app.extConfig) === null || _d === void 0 ? void 0 : _d.cordova;
}
}
}
else {
cordova = (_e = config.app.extConfig) === null || _e === void 0 ? void 0 : _e.cordova;
}
return cordova;
}
exports.getCordovaPreferences = getCordovaPreferences;
async function writeCordovaAndroidManifest(cordovaPlugins, config, platform, cleartext) {
var _a;
const manifestPath = (0, path_1.join)(config.android.cordovaPluginsDirAbs, 'src', 'main', 'AndroidManifest.xml');
const rootXMLEntries = [];
const applicationXMLEntries = [];
const applicationXMLAttributes = [];
let prefsArray = [];
cordovaPlugins.map(async (p) => {
const editConfig = (0, plugin_1.getPlatformElement)(p, platform, 'edit-config');
const configFile = (0, plugin_1.getPlatformElement)(p, platform, 'config-file');
prefsArray = prefsArray.concat((0, plugin_1.getAllElements)(p, platform, 'preference'));
editConfig.concat(configFile).map(async (configElement) => {
var _a, _b;
if (configElement.$ &&
(((_a = configElement.$.target) === null || _a === void 0 ? void 0 : _a.includes('AndroidManifest.xml')) ||
((_b = configElement.$.file) === null || _b === void 0 ? void 0 : _b.includes('AndroidManifest.xml')))) {
const keys = Object.keys(configElement).filter(k => k !== '$');
keys.map(k => {
configElement[k].map(async (e) => {
const xmlElement = (0, xml_1.buildXmlElement)(e, k);
const pathParts = getPathParts(configElement.$.parent || configElement.$.target);
if (pathParts.length > 1) {
if (pathParts.pop() === 'application') {
if (configElement.$.mode &&
configElement.$.mode === 'merge' &&
xmlElement.startsWith('<application')) {
Object.keys(e.$).map((ek) => {
applicationXMLAttributes.push(`${ek}="${e.$[ek]}"`);
});
}
else if (!applicationXMLEntries.includes(xmlElement) &&
!contains(applicationXMLEntries, xmlElement, k)) {
applicationXMLEntries.push(xmlElement);
}
}
else {
const manifestPathOfCapApp = (0, path_1.join)(config.android.appDirAbs, 'src', 'main', 'AndroidManifest.xml');
const manifestContentTrimmed = (await (0, utils_fs_1.readFile)(manifestPathOfCapApp))
.toString()
.trim()
.replace(/\n|\t|\r/g, '')
.replace(/[\s]{1,}</g, '<')
.replace(/>[\s]{1,}/g, '>')
.replace(/[\s]{2,}/g, ' ');
const requiredManifestContentTrimmed = xmlElement
.trim()
.replace(/\n|\t|\r/g, '')
.replace(/[\s]{1,}</g, '<')
.replace(/>[\s]{1,}/g, '>')
.replace(/[\s]{2,}/g, ' ');
const pathPartList = getPathParts(configElement.$.parent || configElement.$.target);
const doesXmlManifestContainRequiredInfo = (requiredElements, existingElements, pathTarget) => {
const findElementsToSearchIn = (existingElements, pathTarget) => {
const parts = [...pathTarget];
const elementsToSearchNextIn = [];
for (const existingElement of existingElements) {
if (existingElement.name === pathTarget[0]) {
if (existingElement.children) {
for (const el of existingElement.children) {
elementsToSearchNextIn.push(el);
}
}
else {
elementsToSearchNextIn.push(existingElement);
}
}
}
if (elementsToSearchNextIn.length === 0) {
return [];
}
else {
parts.splice(0, 1);
if (parts.length <= 0) {
return elementsToSearchNextIn;
}
else {
return findElementsToSearchIn(elementsToSearchNextIn, parts);
}
}
};
const parseXmlToSearchable = (childElementsObj, arrayToAddTo) => {
for (const childElementKey of Object.keys(childElementsObj)) {
for (const occurannceOfElement of childElementsObj[childElementKey]) {
const toAdd = { name: childElementKey };
if (occurannceOfElement['$']) {
toAdd.attrs = { ...occurannceOfElement['$'] };
}
if (occurannceOfElement['$$']) {
toAdd.children = [];
parseXmlToSearchable(occurannceOfElement['$$'], toAdd['children']);
}
arrayToAddTo.push(toAdd);
}
}
};
const doesElementMatch = (requiredElement, existingElement) => {
var _a;
if (requiredElement.name !== existingElement.name) {
return false;
}
if ((requiredElement.attrs !== undefined) !==
(existingElement.attrs !== undefined)) {
return false;
}
else {
if (requiredElement.attrs !== undefined) {
const requiredELementAttrKeys = Object.keys(requiredElement.attrs);
for (const key of requiredELementAttrKeys) {
if (!/^[$].{1,}$/.test(requiredElement.attrs[key].trim())) {
if (requiredElement.attrs[key] !==
existingElement.attrs[key]) {
return false;
}
}
}
}
}
if ((requiredElement.children !== undefined) !==
(existingElement.children !== undefined) &&
((_a = requiredElement.children) === null || _a === void 0 ? void 0 : _a.length) !== 0) {
return false;
}
else {
if (requiredElement.children !== undefined) {
// each req element is in existing element
for (const requiredElementItem of requiredElement.children) {
let foundRequiredElement = false;
for (const existingElementItem of existingElement.children) {
const foundRequiredElementIn = doesElementMatch(requiredElementItem, existingElementItem);
if (foundRequiredElementIn) {
foundRequiredElement = true;
break;
}
}
if (!foundRequiredElement) {
return false;
}
}
}
else {
if (requiredElement.children === undefined &&
existingElement.children === undefined) {
return true;
}
else {
let foundRequiredElement = false;
for (const existingElementItem of existingElement.children) {
const foundRequiredElementIn = doesElementMatch(requiredElement, existingElementItem);
if (foundRequiredElementIn) {
foundRequiredElement = true;
break;
}
}
if (!foundRequiredElement) {
return false;
}
}
}
}
return true;
};
const parsedExistingElements = [];
const rootKeyOfExistingElements = Object.keys(existingElements)[0];
const rootOfExistingElementsToAdd = { name: rootKeyOfExistingElements, children: [] };
if (existingElements[rootKeyOfExistingElements]['$']) {
rootOfExistingElementsToAdd.attrs = {
...existingElements[rootKeyOfExistingElements]['$'],
};
}
parseXmlToSearchable(existingElements[rootKeyOfExistingElements]['$$'], rootOfExistingElementsToAdd['children']);
parsedExistingElements.push(rootOfExistingElementsToAdd);
const parsedRequiredElements = [];
const rootKeyOfRequiredElements = Object.keys(requiredElements)[0];
const rootOfRequiredElementsToAdd = { name: rootKeyOfRequiredElements, children: [] };
if (requiredElements[rootKeyOfRequiredElements]['$']) {
rootOfRequiredElementsToAdd.attrs = {
...requiredElements[rootKeyOfRequiredElements]['$'],
};
}
if (requiredElements[rootKeyOfRequiredElements]['$$'] !==
undefined) {
parseXmlToSearchable(requiredElements[rootKeyOfRequiredElements]['$$'], rootOfRequiredElementsToAdd['children']);
}
parsedRequiredElements.push(rootOfRequiredElementsToAdd);
const elementsToSearch = findElementsToSearchIn(parsedExistingElements, pathTarget);
for (const requiredElement of parsedRequiredElements) {
let foundMatch = false;
for (const existingElement of elementsToSearch) {
const doesContain = doesElementMatch(requiredElement, existingElement);
if (doesContain) {
foundMatch = true;
break;
}
}
if (!foundMatch) {
return false;
}
}
return true;
};
if (!doesXmlManifestContainRequiredInfo((0, xml_1.parseXML)(requiredManifestContentTrimmed, {
explicitChildren: true,
trim: true,
}), (0, xml_1.parseXML)(manifestContentTrimmed, {
explicitChildren: true,
trim: true,
}), pathPartList)) {
log_1.logger.warn(`Android Configuration required for ${colors_1.default.strong(p.id)}.\n` +
`Add the following to AndroidManifest.xml:\n` +
xmlElement);
}
}
}
else {
if (!rootXMLEntries.includes(xmlElement) &&
!contains(rootXMLEntries, xmlElement, k)) {
rootXMLEntries.push(xmlElement);
}
}
});
});
}
});
});
const cleartextString = 'android:usesCleartextTraffic="true"';
const cleartextValue = (cleartext || ((_a = config.app.extConfig.server) === null || _a === void 0 ? void 0 : _a.cleartext)) &&
!applicationXMLAttributes.includes(cleartextString)
? cleartextString
: '';
let content = `<?xml version='1.0' encoding='utf-8'?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:amazon="http://schemas.amazon.com/apk/res/android">
<application ${applicationXMLAttributes.join('\n')} ${cleartextValue}>
${applicationXMLEntries.join('\n')}
</application>
${rootXMLEntries.join('\n')}
</manifest>`;
content = content.replace(new RegExp('$PACKAGE_NAME'.replace('$', '\\$&'), 'g'), '${applicationId}');
for (const preference of prefsArray) {
content = content.replace(new RegExp(('$' + preference.$.name).replace('$', '\\$&'), 'g'), preference.$.default);
}
if (await (0, utils_fs_1.pathExists)(manifestPath)) {
await (0, utils_fs_1.writeFile)(manifestPath, content);
}
}
exports.writeCordovaAndroidManifest = writeCordovaAndroidManifest;
function getPathParts(path) {
const rootPath = 'manifest';
path = path.replace('/*', rootPath);
const parts = path.split('/').filter(part => part !== '');
if (parts.length > 1 || parts.includes(rootPath)) {
return parts;
}
return [rootPath, path];
}
function contains(entries, obj, k) {
const element = (0, xml_1.parseXML)(obj);
for (const entry of entries) {
const current = (0, xml_1.parseXML)(entry);
if (element &&
current &&
current[k] &&
element[k] &&
current[k].$ &&
element[k].$ &&
element[k].$['android:name'] === current[k].$['android:name']) {
return true;
}
}
return false;
}

View file

@ -0,0 +1,605 @@
export interface CapacitorConfig {
/**
* The unique identifier of your packaged app.
*
* This is also known as the Bundle ID in iOS and the Application ID in
* Android. It must be in reverse domain name notation, generally
* representing a domain name that you or your company owns.
*
* @since 1.0.0
*/
appId?: string;
/**
* The human-friendly name of your app.
*
* This should be what you'd see in the App Store, but can be changed after
* within each native platform after it is generated.
*
* @since 1.0.0
*/
appName?: string;
/**
* The directory of your compiled web assets.
*
* This directory should contain the final `index.html` of your app.
*
* @since 1.0.0
*/
webDir?: string;
/**
* Whether to copy the Capacitor runtime bundle or not.
*
* If your app is not using a bundler, set this to `true`, then Capacitor
* will create a `capacitor.js` file that you'll need to add as a script in
* your `index.html` file.
*
* It's deprecated and will be removed in Capacitor 6
*
* @since 1.0.0
* @deprecated 5.0.0
* @default false
*/
bundledWebRuntime?: boolean;
/**
* The build configuration (as defined by the native app) under which Capacitor
* will send statements to the log system. This applies to log statements in
* native code as well as statements redirected from JavaScript (`console.debug`,
* `console.error`, etc.). Enabling logging will let statements render in the
* Xcode and Android Studio windows but can leak information on device if enabled
* in released builds.
*
* 'none' = logs are never produced
* 'debug' = logs are produced in debug builds but not production builds
* 'production' = logs are always produced
*
* @since 3.0.0
* @default debug
*/
loggingBehavior?: 'none' | 'debug' | 'production';
/**
* User agent of Capacitor Web View.
*
* @since 1.4.0
*/
overrideUserAgent?: string;
/**
* String to append to the original user agent of Capacitor Web View.
*
* This is disregarded if `overrideUserAgent` is used.
*
* @since 1.4.0
*/
appendUserAgent?: string;
/**
* Background color of the Capacitor Web View.
*
* @since 1.1.0
*/
backgroundColor?: string;
android?: {
/**
* Specify a custom path to the native Android project.
*
* @since 3.0.0
* @default android
*/
path?: string;
/**
* User agent of Capacitor Web View on Android.
*
* Overrides global `overrideUserAgent` option.
*
* @since 1.4.0
*/
overrideUserAgent?: string;
/**
* String to append to the original user agent of Capacitor Web View for Android.
*
* Overrides global `appendUserAgent` option.
*
* This is disregarded if `overrideUserAgent` is used.
*
* @since 1.4.0
*/
appendUserAgent?: string;
/**
* Background color of the Capacitor Web View for Android.
*
* Overrides global `backgroundColor` option.
*
* @since 1.1.0
*/
backgroundColor?: string;
/**
* Enable mixed content in the Capacitor Web View for Android.
*
* [Mixed
* content](https://developer.mozilla.org/en-US/docs/Web/Security/Mixed_content)
* is disabled by default for security. During development, you may need to
* enable it to allow the Web View to load files from different schemes.
*
* **This is not intended for use in production.**
*
* @since 1.0.0
* @default false
*/
allowMixedContent?: boolean;
/**
* This enables a simpler keyboard which may have some limitations.
*
* This will capture JS keys using an alternative
* [`InputConnection`](https://developer.android.com/reference/android/view/inputmethod/InputConnection).
*
* @since 1.0.0
* @default false
*/
captureInput?: boolean;
/**
* Always enable debuggable web content.
*
* This is automatically enabled during development.
*
* @since 1.0.0
* @default false
*/
webContentsDebuggingEnabled?: boolean;
/**
* The build configuration under which Capacitor will generate logs on Android.
*
* Overrides global `loggingBehavior` option.
*
* @since 3.0.0
* @default debug
*/
loggingBehavior?: 'none' | 'debug' | 'production';
/**
* Allowlist of plugins to include during `npx cap sync` for Android.
*
* Overrides global `includePlugins` option.
*
* @since 3.0.0
*/
includePlugins?: string[];
/**
* Android flavor to use.
*
* If the app has flavors declared in the `build.gradle`
* configure the flavor you want to run with `npx cap run` command.
*
* @since 3.1.0
*/
flavor?: string;
/**
* Whether to give the webview initial focus.
*
* @since 3.5.1
* @default true
*/
initialFocus?: boolean;
/**
* The minimum supported webview version on Android supported by your app.
*
* The minimum supported cannot be lower than version `55`, which is required for Capacitor.
*
* If the device uses a lower WebView version, an error message will be shown on Logcat.
* If `server.errorPath` is configured, the WebView will redirect to that file, so can be
* used to show a custom error.
*
* @since 4.0.0
* @default 60
*/
minWebViewVersion?: number;
/**
* The minimum supported Huawei webview version on Android supported by your app.
*
* The minimum supported cannot be lower than version `10`, which is required for Capacitor.
*
* If the device uses a lower WebView version, an error message will be shown on Logcat.
* If `server.errorPath` is configured, the WebView will redirect to that file, so can be
* used to show a custom error.
*
* @since 4.6.4
* @default 10
*/
minHuaweiWebViewVersion?: number;
buildOptions?: {
/**
* Path to your keystore
*
* @since 4.4.0
*/
keystorePath?: string;
/**
* Password to your keystore
*
* @since 4.4.0
*/
keystorePassword?: string;
/**
* Alias in the keystore to use
*
* @since 4.4.0
*/
keystoreAlias?: string;
/**
* Password for the alias in the keystore to use
*
* @since 4.4.0
*/
keystoreAliasPassword?: string;
/**
* Bundle type for your release build
*
* @since 4.4.0
* @default "AAB"
*/
releaseType?: 'AAB' | 'APK';
/**
* Program to sign your build with
*
* @since 5.1.0
* @default "jarsigner"
*/
signingType?: 'apksigner' | 'jarsigner';
};
/**
* Use legacy [addJavascriptInterface](https://developer.android.com/reference/android/webkit/WebView#addJavascriptInterface(java.lang.Object,%20java.lang.String))
* instead of the new and more secure [addWebMessageListener](https://developer.android.com/reference/androidx/webkit/WebViewCompat#addWebMessageListener(android.webkit.WebView,java.lang.String,java.util.Set%3Cjava.lang.String%3E,androidx.webkit.WebViewCompat.WebMessageListener))
*
* @since 4.5.0
* @default false
*/
useLegacyBridge?: boolean;
};
ios?: {
/**
* Specify a custom path to the native iOS project.
*
* @since 3.0.0
* @default ios
*/
path?: string;
/**
* iOS build scheme to use.
*
* Usually this matches your app's target in Xcode. You can use the
* following command to list schemes:
*
* ```shell
* xcodebuild -workspace ios/App/App.xcworkspace -list
* ```
*
* @since 3.0.0
* @default App
*/
scheme?: string;
/**
* User agent of Capacitor Web View on iOS.
*
* Overrides global `overrideUserAgent` option.
*
* @since 1.4.0
*/
overrideUserAgent?: string;
/**
* String to append to the original user agent of Capacitor Web View for iOS.
*
* Overrides global `appendUserAgent` option.
*
* This is disregarded if `overrideUserAgent` is used.
*
* @since 1.4.0
*/
appendUserAgent?: string;
/**
* Background color of the Capacitor Web View for iOS.
*
* Overrides global `backgroundColor` option.
*
* @since 1.1.0
*/
backgroundColor?: string;
/**
* Configure the scroll view's content inset adjustment behavior.
*
* This will set the
* [`contentInsetAdjustmentBehavior`](https://developer.apple.com/documentation/uikit/uiscrollview/2902261-contentinsetadjustmentbehavior)
* property on the Web View's
* [`UIScrollView`](https://developer.apple.com/documentation/uikit/uiscrollview).
*
* @since 2.0.0
* @default never
*/
contentInset?: 'automatic' | 'scrollableAxes' | 'never' | 'always';
/**
* Configure whether the scroll view is scrollable.
*
* This will set the
* [`isScrollEnabled`](https://developer.apple.com/documentation/uikit/uiscrollview/1619395-isscrollenabled)
* property on the Web View's
* [`UIScrollView`](https://developer.apple.com/documentation/uikit/uiscrollview).
*
* @since 1.0.0
*/
scrollEnabled?: boolean;
/**
* Configure custom linker flags for compiling Cordova plugins.
*
* @since 1.0.0
* @default []
*/
cordovaLinkerFlags?: string[];
/**
* Allow destination previews when pressing on links.
*
* This will set the
* [`allowsLinkPreview`](https://developer.apple.com/documentation/webkit/wkwebview/1415000-allowslinkpreview)
* property on the Web View, instead of using the default value.
*
* @since 2.0.0
*/
allowsLinkPreview?: boolean;
/**
* The build configuration under which Capacitor will generate logs on iOS.
*
* Overrides global `loggingBehavior` option.
*
* @since 3.0.0
* @default debug
*/
loggingBehavior?: 'none' | 'debug' | 'production';
/**
* Allowlist of plugins to include during `npx cap sync` for iOS.
*
* Overrides global `includePlugins` option.
*
* @since 3.0.0
*/
includePlugins?: string[];
/**
* Sets WKWebView configuration for limitsNavigationsToAppBoundDomains.
*
* If the Info.plist file includes `WKAppBoundDomains` key, it's recommended to
* set this option to true, otherwise some features won't work.
* But as side effect, it blocks navigation outside the domains in the
* `WKAppBoundDomains` list.
* `localhost` (or the value configured as `server.hostname`) also needs to be
* added to the `WKAppBoundDomains` list.
*
* @since 3.1.0
* @default false
*/
limitsNavigationsToAppBoundDomains?: boolean;
/**
* The content mode for the web view to use when it loads and renders web content.
*
* - 'recommended': The content mode that is appropriate for the current device.
* - 'desktop': The content mode that represents a desktop experience.
* - 'mobile': The content mode that represents a mobile experience.
*
* @since 4.0.0
* @default recommended
*/
preferredContentMode?: 'recommended' | 'desktop' | 'mobile';
/**
* Configure if Capacitor will handle local/push notifications.
* Set to false if you want to use your own UNUserNotificationCenter to handle notifications.
*
* @since 4.5.0
* @default true
*/
handleApplicationNotifications?: boolean;
/**
* Using Xcode 14.3, on iOS 16.4 and greater, enable debuggable web content for release builds.
*
* If not set, it's `true` for development builds.
*
* @since 4.8.0
* @default false
*/
webContentsDebuggingEnabled?: boolean;
};
server?: {
/**
* Configure the local hostname of the device.
*
* It is recommended to keep this as `localhost` as it allows the use of
* Web APIs that would otherwise require a [secure
* context](https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts)
* such as
* [`navigator.geolocation`](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/geolocation)
* and
* [`MediaDevices.getUserMedia`](https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia).
*
* @since 1.0.0
* @default localhost
*/
hostname?: string;
/**
* Configure the local scheme on iOS.
*
* [Can't be set to schemes that the WKWebView already handles, such as http or https](https://developer.apple.com/documentation/webkit/wkwebviewconfiguration/2875766-seturlschemehandler)
* This can be useful when migrating from
* [`cordova-plugin-ionic-webview`](https://github.com/ionic-team/cordova-plugin-ionic-webview),
* where the default scheme on iOS is `ionic`.
*
* @since 1.2.0
* @default capacitor
*/
iosScheme?: string;
/**
* Configure the local scheme on Android.
*
* Custom schemes on Android are unable to change the URL path as of Webview 117. Changing this value from anything other than `http` or `https` can result in your
* application unable to resolve routing. If you must change this for some reason, consider using a hash-based url strategy, but there are no guarentees that this
* will continue to work long term as allowing non-standard schemes to modify query parameters and url fragments is only allowed for compatibility reasons.
* https://ionic.io/blog/capacitor-android-customscheme-issue-with-chrome-117
*
* @since 1.2.0
* @default http
*/
androidScheme?: string;
/**
* Load an external URL in the Web View.
*
* This is intended for use with live-reload servers.
*
* **This is not intended for use in production.**
*
* @since 1.0.0
*/
url?: string;
/**
* Allow cleartext traffic in the Web View.
*
* On Android, all cleartext traffic is disabled by default as of API 28.
*
* This is intended for use with live-reload servers where unencrypted HTTP
* traffic is often used.
*
* **This is not intended for use in production.**
*
* @since 1.5.0
* @default false
*/
cleartext?: boolean;
/**
* Set additional URLs the Web View can navigate to.
*
* By default, all external URLs are opened in the external browser (not
* the Web View).
*
* **This is not intended for use in production.**
*
* @since 1.0.0
* @default []
*/
allowNavigation?: string[];
/**
* Specify path to a local html page to display in case of errors.
* On Android the html file won't have access to Capacitor plugins.
*
* @since 4.0.0
* @default null
*/
errorPath?: string;
};
cordova?: {
/**
* Populates <access> tags in the config.xml with the origin set to
* the values entered here.
* If not provided, a single <access origin="*" /> tag gets included.
* It only has effect on a few Cordova plugins that respect the whitelist.
*
* @since 3.3.0
*/
accessOrigins?: string[];
/**
* Configure Cordova preferences.
*
* @since 1.3.0
*/
preferences?: {
[key: string]: string | undefined;
};
/**
* List of Cordova plugins that need to be static but are not
* already in the static plugin list.
*
* @since 3.3.0
*/
staticPlugins?: string[];
};
/**
* Configure plugins.
*
* This is an object with configuration values specified by plugin class
* name.
*
* @since 1.0.0
*/
plugins?: PluginsConfig;
/**
* Allowlist of plugins to include during `npx cap sync`.
*
* This should be an array of strings representing the npm package name of
* plugins to include when running `npx cap sync`. If unset, Capacitor will
* inspect `package.json` for a list of potential plugins.
*
* @since 3.0.0
*/
includePlugins?: string[];
}
export interface FederatedApp {
name: string;
webDir: string;
liveUpdateConfig?: LiveUpdateConfig;
}
export interface LiveUpdateConfig {
appId: string;
channel: string;
autoUpdateMethod: AutoUpdateMethod;
maxVersions?: number;
key?: string;
}
export type AutoUpdateMethod = 'none' | 'background';
export interface PluginsConfig {
/**
* Plugin configuration by class name.
*
* @since 1.0.0
*/
[key: string]: {
[key: string]: any;
} | undefined;
/**
* FederatedCapacitor plugin configuration
*
* @since 5.0.0
*/
FederatedCapacitor?: {
shell: Omit<FederatedApp, 'webDir'>;
apps: FederatedApp[];
liveUpdatesKey?: string;
};
/**
* Capacitor Live Updates plugin configuration
*
* @since 4.2.0
*/
LiveUpdates?: LiveUpdateConfig;
/**
* Capacitor Cookies plugin configuration
*
* @since 4.3.0
*/
CapacitorCookies?: {
/**
* Enable CapacitorCookies to override the global `document.cookie` on native.
*
* @default false
*/
enabled?: boolean;
/**
* Enable `httpOnly` and other insecure cookies to be read and accessed on Android.
*
* Note: This can potentially be a security risk and is only intended to be used
* when your application uses a custom scheme on Android.
*
*/
androidCustomSchemeAllowInsecureAccess?: boolean;
};
/**
* Capacitor Http plugin configuration
*
* @since 4.3.0
*/
CapacitorHttp?: {
/**
* Enable CapacitorHttp to override the global `fetch` and `XMLHttpRequest` on native.
*
* @default false
*/
enabled?: boolean;
};
}

View file

@ -0,0 +1,2 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });

View file

@ -0,0 +1,2 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });

View file

@ -0,0 +1,27 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.isFatal = exports.fatal = exports.FatalException = exports.BaseException = void 0;
class BaseException extends Error {
constructor(message, code) {
super(message);
this.message = message;
this.code = code;
}
}
exports.BaseException = BaseException;
class FatalException extends BaseException {
constructor(message, exitCode = 1) {
super(message, 'FATAL');
this.message = message;
this.exitCode = exitCode;
}
}
exports.FatalException = FatalException;
function fatal(message) {
throw new FatalException(message);
}
exports.fatal = fatal;
function isFatal(e) {
return e && e instanceof FatalException;
}
exports.isFatal = isFatal;

View file

@ -0,0 +1,109 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.detectFramework = void 0;
const FRAMEWORK_CONFIGS = [
{
name: 'Angular',
isMatch: config => hasDependency(config, '@angular/cli'),
webDir: 'dist',
priority: 3,
},
{
name: 'Create React App',
isMatch: config => hasDependency(config, 'react-scripts'),
webDir: 'build',
priority: 3,
},
{
name: 'Ember',
isMatch: config => hasDependency(config, 'ember-cli'),
webDir: 'dist',
priority: 3,
},
{
name: 'Gatsby',
isMatch: config => hasDependency(config, 'gatsby'),
webDir: 'public',
priority: 2,
},
{
name: 'Ionic Angular',
isMatch: config => hasDependency(config, '@ionic/angular'),
webDir: 'www',
priority: 1,
},
{
name: 'Ionic React',
isMatch: config => hasDependency(config, '@ionic/react'),
webDir: 'build',
priority: 1,
},
{
name: 'Ionic Vue',
isMatch: config => hasDependency(config, '@ionic/vue'),
webDir: 'public',
priority: 1,
},
{
name: 'Next',
isMatch: config => hasDependency(config, 'next'),
webDir: 'public',
priority: 2,
},
{
name: 'Preact',
isMatch: config => hasDependency(config, 'preact-cli'),
webDir: 'build',
priority: 3,
},
{
name: 'Stencil',
isMatch: config => hasDependency(config, '@stencil/core'),
webDir: 'www',
priority: 3,
},
{
name: 'Svelte',
isMatch: config => hasDependency(config, 'svelte') && hasDependency(config, 'sirv-cli'),
webDir: 'public',
priority: 3,
},
{
name: 'Vite',
isMatch: config => hasDependency(config, 'vite'),
webDir: 'dist',
priority: 2,
},
{
name: 'Vue',
isMatch: config => hasDependency(config, '@vue/cli-service'),
webDir: 'dist',
priority: 3,
},
];
function detectFramework(config) {
const frameworks = FRAMEWORK_CONFIGS.filter(f => f.isMatch(config)).sort((a, b) => {
if (a.priority < b.priority)
return -1;
if (a.priority > b.priority)
return 1;
return 0;
});
return frameworks[0];
}
exports.detectFramework = detectFramework;
function hasDependency(config, depName) {
const deps = getDependencies(config);
return deps.includes(depName);
}
function getDependencies(config) {
var _a, _b, _c, _d;
const deps = [];
if ((_b = (_a = config === null || config === void 0 ? void 0 : config.app) === null || _a === void 0 ? void 0 : _a.package) === null || _b === void 0 ? void 0 : _b.dependencies) {
deps.push(...Object.keys(config.app.package.dependencies));
}
if ((_d = (_c = config === null || config === void 0 ? void 0 : config.app) === null || _c === void 0 ? void 0 : _c.package) === null || _d === void 0 ? void 0 : _d.devDependencies) {
deps.push(...Object.keys(config.app.package.devDependencies));
}
return deps;
}

View file

@ -0,0 +1,212 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.runProgram = exports.run = void 0;
const tslib_1 = require("tslib");
const commander_1 = require("commander");
const colors_1 = tslib_1.__importDefault(require("./colors"));
const config_1 = require("./config");
const errors_1 = require("./errors");
const ipc_1 = require("./ipc");
const log_1 = require("./log");
const telemetry_1 = require("./telemetry");
const cli_1 = require("./util/cli");
const emoji_1 = require("./util/emoji");
process.on('unhandledRejection', error => {
console.error(colors_1.default.failure('[fatal]'), error);
});
process.on('message', ipc_1.receive);
async function run() {
try {
const config = await (0, config_1.loadConfig)();
runProgram(config);
}
catch (e) {
process.exitCode = (0, errors_1.isFatal)(e) ? e.exitCode : 1;
log_1.logger.error(e.message ? e.message : String(e));
}
}
exports.run = run;
function runProgram(config) {
commander_1.program.version(config.cli.package.version);
commander_1.program
.command('config', { hidden: true })
.description(`print evaluated Capacitor config`)
.option('--json', 'Print in JSON format')
.action((0, cli_1.wrapAction)(async ({ json }) => {
const { configCommand } = await Promise.resolve().then(() => tslib_1.__importStar(require('./tasks/config')));
await configCommand(config, json);
}));
commander_1.program
.command('create [directory] [name] [id]', { hidden: true })
.description('Creates a new Capacitor project')
.action((0, cli_1.wrapAction)(async () => {
const { createCommand } = await Promise.resolve().then(() => tslib_1.__importStar(require('./tasks/create')));
await createCommand();
}));
commander_1.program
.command('init [appName] [appId]')
.description(`Initialize Capacitor configuration`)
.option('--web-dir <value>', 'Optional: Directory of your projects built web assets')
.action((0, cli_1.wrapAction)((0, telemetry_1.telemetryAction)(config, async (appName, appId, { webDir }) => {
const { initCommand } = await Promise.resolve().then(() => tslib_1.__importStar(require('./tasks/init')));
await initCommand(config, appName, appId, webDir);
})));
commander_1.program
.command('serve', { hidden: true })
.description('Serves a Capacitor Progressive Web App in the browser')
.action((0, cli_1.wrapAction)(async () => {
const { serveCommand } = await Promise.resolve().then(() => tslib_1.__importStar(require('./tasks/serve')));
await serveCommand();
}));
commander_1.program
.command('sync [platform]')
.description(`${colors_1.default.input('copy')} + ${colors_1.default.input('update')}`)
.option('--deployment', 'Optional: if provided, pod install will use --deployment option')
.option('--inline', 'Optional: if true, all source maps will be inlined for easier debugging on mobile devices', false)
.action((0, cli_1.wrapAction)((0, telemetry_1.telemetryAction)(config, async (platform, { deployment, inline }) => {
(0, config_1.checkExternalConfig)(config.app);
const { syncCommand } = await Promise.resolve().then(() => tslib_1.__importStar(require('./tasks/sync')));
await syncCommand(config, platform, deployment, inline);
})));
commander_1.program
.command('update [platform]')
.description(`updates the native plugins and dependencies based on ${colors_1.default.strong('package.json')}`)
.option('--deployment', 'Optional: if provided, pod install will use --deployment option')
.action((0, cli_1.wrapAction)((0, telemetry_1.telemetryAction)(config, async (platform, { deployment }) => {
(0, config_1.checkExternalConfig)(config.app);
const { updateCommand } = await Promise.resolve().then(() => tslib_1.__importStar(require('./tasks/update')));
await updateCommand(config, platform, deployment);
})));
commander_1.program
.command('copy [platform]')
.description('copies the web app build into the native app')
.option('--inline', 'Optional: if true, all source maps will be inlined for easier debugging on mobile devices', false)
.action((0, cli_1.wrapAction)((0, telemetry_1.telemetryAction)(config, async (platform, { inline }) => {
(0, config_1.checkExternalConfig)(config.app);
const { copyCommand } = await Promise.resolve().then(() => tslib_1.__importStar(require('./tasks/copy')));
await copyCommand(config, platform, inline);
})));
commander_1.program
.command('build <platform>')
.description('builds the release version of the selected platform')
.option('--scheme <schemeToBuild>', 'iOS Scheme to build')
.option('--flavor <flavorToBuild>', 'Android Flavor to build')
.option('--keystorepath <keystorePath>', 'Path to the keystore')
.option('--keystorepass <keystorePass>', 'Password to the keystore')
.option('--keystorealias <keystoreAlias>', 'Key Alias in the keystore')
.option('--keystorealiaspass <keystoreAliasPass>', 'Password for the Key Alias')
.addOption(new commander_1.Option('--androidreleasetype <androidreleasetype>', 'Android release type; APK or AAB').choices(['AAB', 'APK']))
.addOption(new commander_1.Option('--signing-type <signingtype>', 'Program used to sign apps (default: jarsigner)').choices(['apksigner', 'jarsigner']))
.action((0, cli_1.wrapAction)((0, telemetry_1.telemetryAction)(config, async (platform, { scheme, flavor, keystorepath, keystorepass, keystorealias, keystorealiaspass, androidreleasetype, signingType, }) => {
const { buildCommand } = await Promise.resolve().then(() => tslib_1.__importStar(require('./tasks/build')));
await buildCommand(config, platform, {
scheme,
flavor,
keystorepath,
keystorepass,
keystorealias,
keystorealiaspass,
androidreleasetype,
signingtype: signingType,
});
})));
commander_1.program
.command(`run [platform]`)
.description(`runs ${colors_1.default.input('sync')}, then builds and deploys the native app`)
.option('--scheme <schemeName>', 'set the scheme of the iOS project')
.option('--flavor <flavorName>', 'set the flavor of the Android project (flavor dimensions not yet supported)')
.option('--list', 'list targets, then quit')
// TODO: remove once --json is a hidden option (https://github.com/tj/commander.js/issues/1106)
.allowUnknownOption(true)
.option('--target <id>', 'use a specific target')
.option('--no-sync', `do not run ${colors_1.default.input('sync')}`)
.option('--forwardPorts <port:port>', 'Automatically run "adb reverse" for better live-reloading support')
.option('-l, --live-reload', 'Enable Live Reload')
.option('--host <host>', 'Host used for live reload')
.option('--port <port>', 'Port used for live reload')
.action((0, cli_1.wrapAction)((0, telemetry_1.telemetryAction)(config, async (platform, { scheme, flavor, list, target, sync, forwardPorts, liveReload, host, port, }) => {
const { runCommand } = await Promise.resolve().then(() => tslib_1.__importStar(require('./tasks/run')));
await runCommand(config, platform, {
scheme,
flavor,
list,
target,
sync,
forwardPorts,
liveReload,
host,
port,
});
})));
commander_1.program
.command('open [platform]')
.description('opens the native project workspace (Xcode for iOS)')
.action((0, cli_1.wrapAction)((0, telemetry_1.telemetryAction)(config, async (platform) => {
const { openCommand } = await Promise.resolve().then(() => tslib_1.__importStar(require('./tasks/open')));
await openCommand(config, platform);
})));
commander_1.program
.command('add [platform]')
.description('add a native platform project')
.action((0, cli_1.wrapAction)((0, telemetry_1.telemetryAction)(config, async (platform) => {
(0, config_1.checkExternalConfig)(config.app);
const { addCommand } = await Promise.resolve().then(() => tslib_1.__importStar(require('./tasks/add')));
await addCommand(config, platform);
})));
commander_1.program
.command('ls [platform]')
.description('list installed Cordova and Capacitor plugins')
.action((0, cli_1.wrapAction)((0, telemetry_1.telemetryAction)(config, async (platform) => {
(0, config_1.checkExternalConfig)(config.app);
const { listCommand } = await Promise.resolve().then(() => tslib_1.__importStar(require('./tasks/list')));
await listCommand(config, platform);
})));
commander_1.program
.command('doctor [platform]')
.description('checks the current setup for common errors')
.action((0, cli_1.wrapAction)((0, telemetry_1.telemetryAction)(config, async (platform) => {
(0, config_1.checkExternalConfig)(config.app);
const { doctorCommand } = await Promise.resolve().then(() => tslib_1.__importStar(require('./tasks/doctor')));
await doctorCommand(config, platform);
})));
commander_1.program
.command('telemetry [on|off]', { hidden: true })
.description('enable or disable telemetry')
.action((0, cli_1.wrapAction)(async (onOrOff) => {
const { telemetryCommand } = await Promise.resolve().then(() => tslib_1.__importStar(require('./tasks/telemetry')));
await telemetryCommand(onOrOff);
}));
commander_1.program
.command('📡', { hidden: true })
.description('IPC receiver command')
.action(() => {
// no-op: IPC messages are received via `process.on('message')`
});
commander_1.program
.command('plugin:generate', { hidden: true })
.description('start a new Capacitor plugin')
.action((0, cli_1.wrapAction)(async () => {
const { newPluginCommand } = await Promise.resolve().then(() => tslib_1.__importStar(require('./tasks/new-plugin')));
await newPluginCommand();
}));
commander_1.program
.command('migrate')
.option('--noprompt', 'do not prompt for confirmation')
.option('--packagemanager <packageManager>', 'The package manager to use for dependency installs (npm, pnpm, yarn)')
.description('Migrate your current Capacitor app to the latest major version of Capacitor.')
.action((0, cli_1.wrapAction)(async ({ noprompt, packagemanager }) => {
const { migrateCommand } = await Promise.resolve().then(() => tslib_1.__importStar(require('./tasks/migrate')));
await migrateCommand(config, noprompt, packagemanager);
}));
commander_1.program.arguments('[command]').action((0, cli_1.wrapAction)(async (cmd) => {
if (typeof cmd === 'undefined') {
log_1.output.write(`\n ${(0, emoji_1.emoji)('⚡️', '--')} ${colors_1.default.strong('Capacitor - Cross-Platform apps with JavaScript and the Web')} ${(0, emoji_1.emoji)('⚡️', '--')}\n\n`);
commander_1.program.outputHelp();
}
else {
(0, errors_1.fatal)(`Unknown command: ${colors_1.default.input(cmd)}`);
}
}));
commander_1.program.parse(process.argv);
}
exports.runProgram = runProgram;

View file

@ -0,0 +1,13 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.addIOS = void 0;
const tslib_1 = require("tslib");
const colors_1 = tslib_1.__importDefault(require("../colors"));
const common_1 = require("../common");
const template_1 = require("../util/template");
async function addIOS(config) {
await (0, common_1.runTask)(`Adding native Xcode project in ${colors_1.default.strong(config.ios.platformDir)}`, () => {
return (0, template_1.extractTemplate)(config.cli.assets.ios.platformTemplateArchiveAbs, config.ios.platformDirAbs);
});
}
exports.addIOS = addIOS;

View file

@ -0,0 +1,56 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.buildiOS = void 0;
const tslib_1 = require("tslib");
const utils_fs_1 = require("@ionic/utils-fs");
const path_1 = require("path");
const rimraf_1 = tslib_1.__importDefault(require("rimraf"));
const common_1 = require("../common");
const log_1 = require("../log");
const subprocess_1 = require("../util/subprocess");
async function buildiOS(config, buildOptions) {
var _a;
const theScheme = (_a = buildOptions.scheme) !== null && _a !== void 0 ? _a : 'App';
await (0, common_1.runTask)('Building xArchive', async () => (0, subprocess_1.runCommand)('xcodebuild', [
'-workspace',
(0, path_1.basename)(await config.ios.nativeXcodeWorkspaceDirAbs),
'-scheme',
`${theScheme}`,
'-destination',
`generic/platform=iOS`,
'-archivePath',
`${theScheme}.xcarchive`,
'archive',
], {
cwd: config.ios.nativeProjectDirAbs,
}));
const archivePlistContents = `<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>method</key>
<string>app-store</string>
</dict>
</plist>`;
const archivePlistPath = (0, path_1.join)(`${config.ios.nativeProjectDirAbs}`, 'archive.plist');
(0, utils_fs_1.writeFileSync)(archivePlistPath, archivePlistContents);
await (0, common_1.runTask)('Building IPA', async () => (0, subprocess_1.runCommand)('xcodebuild', [
'archive',
'-archivePath',
`${theScheme}.xcarchive`,
'-exportArchive',
'-exportOptionsPlist',
'archive.plist',
'-exportPath',
'output',
'-allowProvisioningUpdates',
], {
cwd: config.ios.nativeProjectDirAbs,
}));
await (0, common_1.runTask)('Cleaning up', async () => {
(0, utils_fs_1.unlinkSync)(archivePlistPath);
rimraf_1.default.sync((0, path_1.join)(config.ios.nativeProjectDirAbs, `${theScheme}.xcarchive`));
});
(0, log_1.logSuccess)(`Successfully generated an IPA at: ${(0, path_1.join)(config.ios.nativeProjectDirAbs, 'output')}`);
}
exports.buildiOS = buildiOS;

View file

@ -0,0 +1,103 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.editProjectSettingsIOS = exports.resolvePlugin = exports.getIOSPlugins = exports.checkCocoaPods = exports.checkBundler = exports.checkIOSPackage = void 0;
const tslib_1 = require("tslib");
const utils_fs_1 = require("@ionic/utils-fs");
const child_process_1 = require("child_process");
const path_1 = require("path");
const colors_1 = tslib_1.__importDefault(require("../colors"));
const common_1 = require("../common");
const cordova_1 = require("../cordova");
const log_1 = require("../log");
const plugin_1 = require("../plugin");
const subprocess_1 = require("../util/subprocess");
async function checkIOSPackage(config) {
return (0, common_1.checkCapacitorPlatform)(config, 'ios');
}
exports.checkIOSPackage = checkIOSPackage;
function execBundler() {
try {
const bundleOutput = (0, child_process_1.execSync)('bundle &> /dev/null ; echo $?');
return parseInt(bundleOutput.toString());
}
catch (e) {
return -1;
}
}
async function checkBundler(config) {
if (config.cli.os === "mac" /* OS.Mac */) {
let bundlerResult = execBundler();
if (bundlerResult === 1) {
// Bundler version is outdated
log_1.logger.info(`Using ${colors_1.default.strong('Gemfile')}: Bundler update needed...`);
await (0, subprocess_1.runCommand)('gem', ['install', 'bundler']);
bundlerResult = execBundler();
}
if (bundlerResult === 0) {
// Bundler in use, all gems current
log_1.logger.info(`Using ${colors_1.default.strong('Gemfile')}: RubyGems bundle installed`);
}
}
return null;
}
exports.checkBundler = checkBundler;
async function checkCocoaPods(config) {
if (!(await (0, subprocess_1.isInstalled)(await config.ios.podPath)) &&
config.cli.os === "mac" /* OS.Mac */) {
return (`CocoaPods is not installed.\n` +
`See this install guide: ${colors_1.default.strong('https://capacitorjs.com/docs/getting-started/environment-setup#homebrew')}`);
}
return null;
}
exports.checkCocoaPods = checkCocoaPods;
async function getIOSPlugins(allPlugins) {
const resolved = await Promise.all(allPlugins.map(async (plugin) => await resolvePlugin(plugin)));
return resolved.filter((plugin) => !!plugin);
}
exports.getIOSPlugins = getIOSPlugins;
async function resolvePlugin(plugin) {
var _a, _b;
const platform = 'ios';
if ((_a = plugin.manifest) === null || _a === void 0 ? void 0 : _a.ios) {
plugin.ios = {
name: plugin.name,
type: 0 /* PluginType.Core */,
path: (_b = plugin.manifest.ios.src) !== null && _b !== void 0 ? _b : platform,
};
}
else if (plugin.xml) {
plugin.ios = {
name: plugin.name,
type: 1 /* PluginType.Cordova */,
path: 'src/' + platform,
};
if ((0, cordova_1.getIncompatibleCordovaPlugins)(platform).includes(plugin.id) ||
!(0, plugin_1.getPluginPlatform)(plugin, platform)) {
plugin.ios.type = 2 /* PluginType.Incompatible */;
}
}
else {
return null;
}
return plugin;
}
exports.resolvePlugin = resolvePlugin;
/**
* Update the native project files with the desired app id and app name
*/
async function editProjectSettingsIOS(config) {
const appId = config.app.appId;
const appName = config.app.appName
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;');
const pbxPath = `${config.ios.nativeXcodeProjDirAbs}/project.pbxproj`;
const plistPath = (0, path_1.resolve)(config.ios.nativeTargetDirAbs, 'Info.plist');
let plistContent = await (0, utils_fs_1.readFile)(plistPath, { encoding: 'utf-8' });
plistContent = plistContent.replace(/<key>CFBundleDisplayName<\/key>[\s\S]?\s+<string>([^<]*)<\/string>/, `<key>CFBundleDisplayName</key>\n <string>${appName}</string>`);
let pbxContent = await (0, utils_fs_1.readFile)(pbxPath, { encoding: 'utf-8' });
pbxContent = pbxContent.replace(/PRODUCT_BUNDLE_IDENTIFIER = ([^;]+)/g, `PRODUCT_BUNDLE_IDENTIFIER = ${appId}`);
await (0, utils_fs_1.writeFile)(plistPath, plistContent, { encoding: 'utf-8' });
await (0, utils_fs_1.writeFile)(pbxPath, pbxContent, { encoding: 'utf-8' });
}
exports.editProjectSettingsIOS = editProjectSettingsIOS;

View file

@ -0,0 +1,49 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.doctorIOS = void 0;
const common_1 = require("../common");
const errors_1 = require("../errors");
const log_1 = require("../log");
const subprocess_1 = require("../util/subprocess");
const common_2 = require("./common");
async function doctorIOS(config) {
var _a;
// DOCTOR ideas for iOS:
// plugin specific warnings
// check cocoapods installed
// check projects exist
// check content in www === ios/www
// check CLI versions
// check plugins versions
// check native project deps are up-to-date === npm install
// check if npm install was updated
// check online datebase of common errors
// check if www folder is empty (index.html does not exist)
try {
await (0, common_1.check)([
() => (0, common_2.checkBundler)(config) || (0, common_2.checkCocoaPods)(config),
() => (0, common_1.checkWebDir)(config),
checkXcode,
]);
(0, log_1.logSuccess)('iOS looking great! 👌');
}
catch (e) {
(0, errors_1.fatal)((_a = e.stack) !== null && _a !== void 0 ? _a : e);
}
}
exports.doctorIOS = doctorIOS;
async function checkXcode() {
if (!(await (0, subprocess_1.isInstalled)('xcodebuild'))) {
return `Xcode is not installed`;
}
// const matches = output.match(/^Xcode (.*)/);
// if (matches && matches.length === 2) {
// const minVersion = '9.0.0';
// const semver = await import('semver');
// console.log(matches[1]);
// if (semver.gt(minVersion, matches[1])) {
// return `Xcode version is too old, ${minVersion} is required`;
// }
// }
return null;
}

View file

@ -0,0 +1,11 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.openIOS = void 0;
const tslib_1 = require("tslib");
const open_1 = tslib_1.__importDefault(require("open"));
const common_1 = require("../common");
async function openIOS(config) {
await (0, open_1.default)(await config.ios.nativeXcodeWorkspaceDirAbs, { wait: false });
await (0, common_1.wait)(3000);
}
exports.openIOS = openIOS;

View file

@ -0,0 +1,38 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.runIOS = void 0;
const tslib_1 = require("tslib");
const debug_1 = tslib_1.__importDefault(require("debug"));
const path_1 = require("path");
const colors_1 = tslib_1.__importDefault(require("../colors"));
const common_1 = require("../common");
const native_run_1 = require("../util/native-run");
const subprocess_1 = require("../util/subprocess");
const debug = (0, debug_1.default)('capacitor:ios:run');
async function runIOS(config, { target: selectedTarget, scheme: selectedScheme }) {
const target = await (0, common_1.promptForPlatformTarget)(await (0, native_run_1.getPlatformTargets)('ios'), selectedTarget);
const runScheme = selectedScheme || config.ios.scheme;
const derivedDataPath = (0, path_1.resolve)(config.ios.platformDirAbs, 'DerivedData', target.id);
const xcodebuildArgs = [
'-workspace',
(0, path_1.basename)(await config.ios.nativeXcodeWorkspaceDirAbs),
'-scheme',
runScheme,
'-configuration',
'Debug',
'-destination',
`id=${target.id}`,
'-derivedDataPath',
derivedDataPath,
];
debug('Invoking xcodebuild with args: %O', xcodebuildArgs);
await (0, common_1.runTask)('Running xcodebuild', async () => (0, subprocess_1.runCommand)('xcrun', ['xcodebuild', ...xcodebuildArgs], {
cwd: config.ios.nativeProjectDirAbs,
}));
const appName = `${runScheme}.app`;
const appPath = (0, path_1.resolve)(derivedDataPath, 'Build/Products', target.virtual ? 'Debug-iphonesimulator' : 'Debug-iphoneos', appName);
const nativeRunArgs = ['ios', '--app', appPath, '--target', target.id];
debug('Invoking native-run with args: %O', nativeRunArgs);
await (0, common_1.runTask)(`Deploying ${colors_1.default.strong(appName)} to ${colors_1.default.input(target.id)}`, async () => (0, native_run_1.runNativeRun)(nativeRunArgs));
}
exports.runIOS = runIOS;

View file

@ -0,0 +1,406 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.installCocoaPodsPlugins = exports.updateIOS = void 0;
const tslib_1 = require("tslib");
const utils_fs_1 = require("@ionic/utils-fs");
const path_1 = require("path");
const colors_1 = tslib_1.__importDefault(require("../colors"));
const common_1 = require("../common");
const cordova_1 = require("../cordova");
const errors_1 = require("../errors");
const log_1 = require("../log");
const plugin_1 = require("../plugin");
const copy_1 = require("../tasks/copy");
const fs_1 = require("../util/fs");
const node_1 = require("../util/node");
const subprocess_1 = require("../util/subprocess");
const template_1 = require("../util/template");
const common_2 = require("./common");
const platform = 'ios';
async function updateIOS(config, deployment) {
const plugins = await getPluginsTask(config);
const capacitorPlugins = plugins.filter(p => (0, plugin_1.getPluginType)(p, platform) === 0 /* PluginType.Core */);
(0, plugin_1.printPlugins)(capacitorPlugins, 'ios');
await removePluginsNativeFiles(config);
const cordovaPlugins = plugins.filter(p => (0, plugin_1.getPluginType)(p, platform) === 1 /* PluginType.Cordova */);
if (cordovaPlugins.length > 0) {
await copyPluginsNativeFiles(config, cordovaPlugins);
}
if (!(await (0, utils_fs_1.pathExists)(await config.ios.webDirAbs))) {
await (0, copy_1.copy)(config, platform);
}
await (0, cordova_1.handleCordovaPluginsJS)(cordovaPlugins, config, platform);
await (0, cordova_1.checkPluginDependencies)(plugins, platform);
await generateCordovaPodspecs(cordovaPlugins, config);
await installCocoaPodsPlugins(config, plugins, deployment);
await (0, cordova_1.logCordovaManualSteps)(cordovaPlugins, config, platform);
const incompatibleCordovaPlugins = plugins.filter(p => (0, plugin_1.getPluginType)(p, platform) === 2 /* PluginType.Incompatible */);
(0, plugin_1.printPlugins)(incompatibleCordovaPlugins, platform, 'incompatible');
await (0, common_1.checkPlatformVersions)(config, platform);
}
exports.updateIOS = updateIOS;
async function installCocoaPodsPlugins(config, plugins, deployment) {
await (0, common_1.runTask)(`Updating iOS native dependencies with ${colors_1.default.input(`${await config.ios.podPath} install`)}`, () => {
return updatePodfile(config, plugins, deployment);
});
}
exports.installCocoaPodsPlugins = installCocoaPodsPlugins;
async function updatePodfile(config, plugins, deployment) {
const dependenciesContent = await generatePodFile(config, plugins);
const relativeCapacitoriOSPath = await getRelativeCapacitoriOSPath(config);
const podfilePath = (0, path_1.join)(config.ios.nativeProjectDirAbs, 'Podfile');
let podfileContent = await (0, utils_fs_1.readFile)(podfilePath, { encoding: 'utf-8' });
podfileContent = podfileContent.replace(/(def capacitor_pods)[\s\S]+?(\nend)/, `$1${dependenciesContent}$2`);
podfileContent = podfileContent.replace(/(require_relative)[\s\S]+?(@capacitor\/ios\/scripts\/pods_helpers')/, `require_relative '${relativeCapacitoriOSPath}/scripts/pods_helpers'`);
podfileContent = podfileContent.replace(`def assertDeploymentTarget(installer)
installer.pods_project.targets.each do |target|
target.build_configurations.each do |config|
# ensure IPHONEOS_DEPLOYMENT_TARGET is at least 13.0
deployment_target = config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'].to_f
should_upgrade = deployment_target < 13.0 && deployment_target != 0.0
if should_upgrade
config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '13.0'
end
end
end
end`, `require_relative '${relativeCapacitoriOSPath}/scripts/pods_helpers'`);
await (0, utils_fs_1.writeFile)(podfilePath, podfileContent, { encoding: 'utf-8' });
const podPath = await config.ios.podPath;
const useBundler = podPath.startsWith('bundle');
const podCommandExists = await (0, subprocess_1.isInstalled)('pod');
if (useBundler || podCommandExists) {
if (useBundler) {
await (0, subprocess_1.runCommand)('bundle', ['exec', 'pod', 'install', ...(deployment ? ['--deployment'] : [])], { cwd: config.ios.nativeProjectDirAbs });
}
else {
await (0, subprocess_1.runCommand)(podPath, ['install', ...(deployment ? ['--deployment'] : [])], { cwd: config.ios.nativeProjectDirAbs });
}
}
else {
log_1.logger.warn('Skipping pod install because CocoaPods is not installed');
}
const isXcodebuildAvailable = await (0, subprocess_1.isInstalled)('xcodebuild');
if (isXcodebuildAvailable) {
await (0, subprocess_1.runCommand)('xcodebuild', ['-project', (0, path_1.basename)(`${config.ios.nativeXcodeProjDirAbs}`), 'clean'], {
cwd: config.ios.nativeProjectDirAbs,
});
}
else {
log_1.logger.warn('Unable to find "xcodebuild". Skipping xcodebuild clean step...');
}
}
async function getRelativeCapacitoriOSPath(config) {
const capacitoriOSPath = (0, node_1.resolveNode)(config.app.rootDir, '@capacitor/ios', 'package.json');
if (!capacitoriOSPath) {
(0, errors_1.fatal)(`Unable to find ${colors_1.default.strong('node_modules/@capacitor/ios')}.\n` +
`Are you sure ${colors_1.default.strong('@capacitor/ios')} is installed?`);
}
return (0, fs_1.convertToUnixPath)((0, path_1.relative)(config.ios.nativeProjectDirAbs, await (0, utils_fs_1.realpath)((0, path_1.dirname)(capacitoriOSPath))));
}
async function generatePodFile(config, plugins) {
const relativeCapacitoriOSPath = await getRelativeCapacitoriOSPath(config);
const capacitorPlugins = plugins.filter(p => (0, plugin_1.getPluginType)(p, platform) === 0 /* PluginType.Core */);
const pods = await Promise.all(capacitorPlugins.map(async (p) => {
if (!p.ios) {
return '';
}
return ` pod '${p.ios.name}', :path => '${(0, fs_1.convertToUnixPath)((0, path_1.relative)(config.ios.nativeProjectDirAbs, await (0, utils_fs_1.realpath)(p.rootPath)))}'\n`;
}));
const cordovaPlugins = plugins.filter(p => (0, plugin_1.getPluginType)(p, platform) === 1 /* PluginType.Cordova */);
cordovaPlugins.map(async (p) => {
const podspecs = (0, plugin_1.getPlatformElement)(p, platform, 'podspec');
podspecs.map((podspec) => {
podspec.pods.map((pPods) => {
pPods.pod.map((pod) => {
if (pod.$.git) {
let gitRef = '';
if (pod.$.tag) {
gitRef = `, :tag => '${pod.$.tag}'`;
}
else if (pod.$.branch) {
gitRef = `, :branch => '${pod.$.branch}'`;
}
else if (pod.$.commit) {
gitRef = `, :commit => '${pod.$.commit}'`;
}
pods.push(` pod '${pod.$.name}', :git => '${pod.$.git}'${gitRef}\n`);
}
});
});
});
});
const staticPlugins = cordovaPlugins.filter(p => (0, cordova_1.needsStaticPod)(p, config));
const noStaticPlugins = cordovaPlugins.filter(el => !staticPlugins.includes(el));
if (noStaticPlugins.length > 0) {
pods.push(` pod 'CordovaPlugins', :path => '../capacitor-cordova-ios-plugins'\n`);
}
if (staticPlugins.length > 0) {
pods.push(` pod 'CordovaPluginsStatic', :path => '../capacitor-cordova-ios-plugins'\n`);
}
const resourcesPlugins = cordovaPlugins.filter(filterResources);
if (resourcesPlugins.length > 0) {
pods.push(` pod 'CordovaPluginsResources', :path => '../capacitor-cordova-ios-plugins'\n`);
}
return `
pod 'Capacitor', :path => '${relativeCapacitoriOSPath}'
pod 'CapacitorCordova', :path => '${relativeCapacitoriOSPath}'
${pods.join('').trimRight()}`;
}
function getFrameworkName(framework) {
if (isFramework(framework)) {
if (framework.$.custom && framework.$.custom === 'true') {
return framework.$.src;
}
return framework.$.src.substr(0, framework.$.src.indexOf('.'));
}
return framework.$.src
.substr(0, framework.$.src.indexOf('.'))
.replace('lib', '');
}
function isFramework(framework) {
return framework.$.src.split('.').pop().includes('framework');
}
async function generateCordovaPodspecs(cordovaPlugins, config) {
const staticPlugins = cordovaPlugins.filter(p => (0, cordova_1.needsStaticPod)(p, config));
const noStaticPlugins = cordovaPlugins.filter(el => !staticPlugins.includes(el));
generateCordovaPodspec(noStaticPlugins, config, false);
generateCordovaPodspec(staticPlugins, config, true);
}
async function generateCordovaPodspec(cordovaPlugins, config, isStatic) {
const weakFrameworks = [];
const linkedFrameworks = [];
const customFrameworks = [];
const systemLibraries = [];
const sourceFrameworks = [];
const frameworkDeps = [];
const compilerFlags = [];
let prefsArray = [];
let name = 'CordovaPlugins';
let sourcesFolderName = 'sources';
if (isStatic) {
name += 'Static';
frameworkDeps.push('s.static_framework = true');
sourcesFolderName += 'static';
}
cordovaPlugins.map((plugin) => {
const frameworks = (0, plugin_1.getPlatformElement)(plugin, platform, 'framework');
frameworks.map((framework) => {
if (!framework.$.type) {
const name = getFrameworkName(framework);
if (isFramework(framework)) {
if (framework.$.weak && framework.$.weak === 'true') {
if (!weakFrameworks.includes(name)) {
weakFrameworks.push(name);
}
}
else if (framework.$.custom && framework.$.custom === 'true') {
const frameworktPath = (0, path_1.join)(sourcesFolderName, plugin.name, name);
if (!customFrameworks.includes(frameworktPath)) {
customFrameworks.push(frameworktPath);
}
}
else {
if (!linkedFrameworks.includes(name)) {
linkedFrameworks.push(name);
}
}
}
else {
if (!systemLibraries.includes(name)) {
systemLibraries.push(name);
}
}
}
else if (framework.$.type && framework.$.type === 'podspec') {
let depString = `s.dependency '${framework.$.src}'`;
if (framework.$.spec && framework.$.spec !== '') {
depString += `, '${framework.$.spec}'`;
}
if (!frameworkDeps.includes(depString)) {
frameworkDeps.push(depString);
}
}
});
prefsArray = prefsArray.concat((0, plugin_1.getAllElements)(plugin, platform, 'preference'));
const podspecs = (0, plugin_1.getPlatformElement)(plugin, platform, 'podspec');
podspecs.map((podspec) => {
podspec.pods.map((pods) => {
pods.pod.map((pod) => {
let depString = `s.dependency '${pod.$.name}'`;
if (pod.$.spec && pod.$.spec !== '') {
depString += `, '${pod.$.spec}'`;
}
if (!frameworkDeps.includes(depString)) {
frameworkDeps.push(depString);
}
});
});
});
const sourceFiles = (0, plugin_1.getPlatformElement)(plugin, platform, 'source-file');
sourceFiles.map((sourceFile) => {
if (sourceFile.$.framework && sourceFile.$.framework === 'true') {
let fileName = sourceFile.$.src.split('/').pop();
if (!fileName.startsWith('lib')) {
fileName = 'lib' + fileName;
}
const frameworktPath = (0, path_1.join)(sourcesFolderName, plugin.name, fileName);
if (!sourceFrameworks.includes(frameworktPath)) {
sourceFrameworks.push(frameworktPath);
}
}
else if (sourceFile.$['compiler-flags']) {
const cFlag = sourceFile.$['compiler-flags'];
if (!compilerFlags.includes(cFlag)) {
compilerFlags.push(cFlag);
}
}
});
});
const onlySystemLibraries = systemLibraries.filter(library => removeNoSystem(library, sourceFrameworks));
if (weakFrameworks.length > 0) {
frameworkDeps.push(`s.weak_frameworks = '${weakFrameworks.join(`', '`)}'`);
}
if (linkedFrameworks.length > 0) {
frameworkDeps.push(`s.frameworks = '${linkedFrameworks.join(`', '`)}'`);
}
if (onlySystemLibraries.length > 0) {
frameworkDeps.push(`s.libraries = '${onlySystemLibraries.join(`', '`)}'`);
}
if (customFrameworks.length > 0) {
frameworkDeps.push(`s.vendored_frameworks = '${customFrameworks.join(`', '`)}'`);
frameworkDeps.push(`s.exclude_files = 'sources/**/*.framework/Headers/*.h', 'sources/**/*.framework/PrivateHeaders/*.h'`);
}
if (sourceFrameworks.length > 0) {
frameworkDeps.push(`s.vendored_libraries = '${sourceFrameworks.join(`', '`)}'`);
}
if (compilerFlags.length > 0) {
frameworkDeps.push(`s.compiler_flags = '${compilerFlags.join(' ')}'`);
}
const arcPlugins = cordovaPlugins.filter(filterARCFiles);
if (arcPlugins.length > 0) {
frameworkDeps.push(`s.subspec 'noarc' do |sna|
sna.requires_arc = false
sna.source_files = 'noarc/**/*.{swift,h,m,c,cc,mm,cpp}'
end`);
}
let frameworksString = frameworkDeps.join('\n ');
frameworksString = await replaceFrameworkVariables(config, prefsArray, frameworksString);
const content = `
Pod::Spec.new do |s|
s.name = '${name}'
s.version = '${config.cli.package.version}'
s.summary = 'Autogenerated spec'
s.license = 'Unknown'
s.homepage = 'https://example.com'
s.authors = { 'Capacitor Generator' => 'hi@example.com' }
s.source = { :git => 'https://github.com/ionic-team/does-not-exist.git', :tag => '${config.cli.package.version}' }
s.source_files = '${sourcesFolderName}/**/*.{swift,h,m,c,cc,mm,cpp}'
s.ios.deployment_target = '${config.ios.minVersion}'
s.xcconfig = {'GCC_PREPROCESSOR_DEFINITIONS' => '$(inherited) COCOAPODS=1 WK_WEB_VIEW_ONLY=1' }
s.dependency 'CapacitorCordova'${getLinkerFlags(config)}
s.swift_version = '5.1'
${frameworksString}
end`;
await (0, utils_fs_1.writeFile)((0, path_1.join)(config.ios.cordovaPluginsDirAbs, `${name}.podspec`), content);
}
function getLinkerFlags(config) {
var _a;
if ((_a = config.app.extConfig.ios) === null || _a === void 0 ? void 0 : _a.cordovaLinkerFlags) {
return `\n s.pod_target_xcconfig = { 'OTHER_LDFLAGS' => '${config.app.extConfig.ios.cordovaLinkerFlags.join(' ')}' }`;
}
return '';
}
async function copyPluginsNativeFiles(config, cordovaPlugins) {
for (const p of cordovaPlugins) {
const sourceFiles = (0, plugin_1.getPlatformElement)(p, platform, 'source-file');
const headerFiles = (0, plugin_1.getPlatformElement)(p, platform, 'header-file');
const codeFiles = sourceFiles.concat(headerFiles);
const frameworks = (0, plugin_1.getPlatformElement)(p, platform, 'framework');
let sourcesFolderName = 'sources';
if ((0, cordova_1.needsStaticPod)(p, config)) {
sourcesFolderName += 'static';
}
const sourcesFolder = (0, path_1.join)(config.ios.cordovaPluginsDirAbs, sourcesFolderName, p.name);
for (const codeFile of codeFiles) {
let fileName = codeFile.$.src.split('/').pop();
const fileExt = codeFile.$.src.split('.').pop();
if (fileExt === 'a' && !fileName.startsWith('lib')) {
fileName = 'lib' + fileName;
}
let destFolder = sourcesFolderName;
if (codeFile.$['compiler-flags'] &&
codeFile.$['compiler-flags'] === '-fno-objc-arc') {
destFolder = 'noarc';
}
const filePath = (0, plugin_1.getFilePath)(config, p, codeFile.$.src);
const fileDest = (0, path_1.join)(config.ios.cordovaPluginsDirAbs, destFolder, p.name, fileName);
await (0, utils_fs_1.copy)(filePath, fileDest);
if (!codeFile.$.framework) {
let fileContent = await (0, utils_fs_1.readFile)(fileDest, { encoding: 'utf-8' });
if (fileExt === 'swift') {
fileContent = 'import Cordova\n' + fileContent;
await (0, utils_fs_1.writeFile)(fileDest, fileContent, { encoding: 'utf-8' });
}
else {
if (fileContent.includes('@import Firebase;')) {
fileContent = fileContent.replace('@import Firebase;', '#import <Firebase/Firebase.h>');
await (0, utils_fs_1.writeFile)(fileDest, fileContent, { encoding: 'utf-8' });
}
if (fileContent.includes('[NSBundle bundleForClass:[self class]]') ||
fileContent.includes('[NSBundle bundleForClass:[CDVCapture class]]')) {
fileContent = fileContent.replace('[NSBundle bundleForClass:[self class]]', '[NSBundle mainBundle]');
fileContent = fileContent.replace('[NSBundle bundleForClass:[CDVCapture class]]', '[NSBundle mainBundle]');
await (0, utils_fs_1.writeFile)(fileDest, fileContent, { encoding: 'utf-8' });
}
if (fileContent.includes('[self.webView superview]') ||
fileContent.includes('self.webView.superview')) {
fileContent = fileContent.replace(/\[self.webView superview\]/g, 'self.viewController.view');
fileContent = fileContent.replace(/self.webView.superview/g, 'self.viewController.view');
await (0, utils_fs_1.writeFile)(fileDest, fileContent, { encoding: 'utf-8' });
}
}
}
}
const resourceFiles = (0, plugin_1.getPlatformElement)(p, platform, 'resource-file');
for (const resourceFile of resourceFiles) {
const fileName = resourceFile.$.src.split('/').pop();
await (0, utils_fs_1.copy)((0, plugin_1.getFilePath)(config, p, resourceFile.$.src), (0, path_1.join)(config.ios.cordovaPluginsDirAbs, 'resources', fileName));
}
for (const framework of frameworks) {
if (framework.$.custom && framework.$.custom === 'true') {
await (0, utils_fs_1.copy)((0, plugin_1.getFilePath)(config, p, framework.$.src), (0, path_1.join)(sourcesFolder, framework.$.src));
}
}
}
}
async function removePluginsNativeFiles(config) {
await (0, utils_fs_1.remove)(config.ios.cordovaPluginsDirAbs);
await (0, template_1.extractTemplate)(config.cli.assets.ios.cordovaPluginsTemplateArchiveAbs, config.ios.cordovaPluginsDirAbs);
}
function filterResources(plugin) {
const resources = (0, plugin_1.getPlatformElement)(plugin, platform, 'resource-file');
return resources.length > 0;
}
function filterARCFiles(plugin) {
const sources = (0, plugin_1.getPlatformElement)(plugin, platform, 'source-file');
const sourcesARC = sources.filter((sourceFile) => sourceFile.$['compiler-flags'] &&
sourceFile.$['compiler-flags'] === '-fno-objc-arc');
return sourcesARC.length > 0;
}
function removeNoSystem(library, sourceFrameworks) {
const libraries = sourceFrameworks.filter(framework => framework.includes(library));
return libraries.length === 0;
}
async function getPluginsTask(config) {
return await (0, common_1.runTask)('Updating iOS plugins', async () => {
const allPlugins = await (0, plugin_1.getPlugins)(config, 'ios');
const iosPlugins = await (0, common_2.getIOSPlugins)(allPlugins);
return iosPlugins;
});
}
async function replaceFrameworkVariables(config, prefsArray, frameworkString) {
prefsArray.map((preference) => {
frameworkString = frameworkString.replace(new RegExp(('$' + preference.$.name).replace('$', '\\$&'), 'g'), preference.$.default);
});
return frameworkString;
}

View file

@ -0,0 +1,61 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.receive = exports.send = void 0;
const tslib_1 = require("tslib");
const utils_fs_1 = require("@ionic/utils-fs");
const utils_subprocess_1 = require("@ionic/utils-subprocess");
const debug_1 = tslib_1.__importDefault(require("debug"));
const https_1 = require("https");
const path_1 = require("path");
const cli_1 = require("./util/cli");
const debug = (0, debug_1.default)('capacitor:ipc');
/**
* Send an IPC message to a forked process.
*/
async function send(msg) {
const dir = cli_1.ENV_PATHS.log;
await (0, utils_fs_1.mkdirp)(dir);
const logPath = (0, path_1.resolve)(dir, 'ipc.log');
debug('Sending %O IPC message to forked process (logs: %O)', msg.type, logPath);
const fd = await (0, utils_fs_1.open)(logPath, 'a');
const p = (0, utils_subprocess_1.fork)(process.argv[1], ['📡'], { stdio: ['ignore', fd, fd, 'ipc'] });
p.send(msg);
p.disconnect();
p.unref();
}
exports.send = send;
/**
* Receive and handle an IPC message.
*
* Assume minimal context and keep external dependencies to a minimum.
*/
async function receive(msg) {
debug('Received %O IPC message', msg.type);
if (msg.type === 'telemetry') {
const now = new Date().toISOString();
const { data } = msg;
// This request is only made if telemetry is on.
const req = (0, https_1.request)({
hostname: 'api.ionicjs.com',
port: 443,
path: '/events/metrics',
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
}, response => {
debug('Sent %O metric to events service (status: %O)', data.name, response.statusCode);
if (response.statusCode !== 204) {
response.on('data', chunk => {
debug('Bad response from events service. Request body: %O', chunk.toString());
});
}
});
const body = {
metrics: [data],
sent_at: now,
};
req.end(JSON.stringify(body));
}
}
exports.receive = receive;

View file

@ -0,0 +1,41 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.logSuccess = exports.logPrompt = exports.logger = exports.output = void 0;
const tslib_1 = require("tslib");
const cli_framework_output_1 = require("@ionic/cli-framework-output");
const colors_1 = tslib_1.__importDefault(require("./colors"));
const term_1 = require("./util/term");
const options = {
colors: colors_1.default,
stream: process.argv.includes('--json') ? process.stderr : process.stdout,
};
exports.output = (0, term_1.isInteractive)()
? new cli_framework_output_1.TTYOutputStrategy(options)
: new cli_framework_output_1.StreamOutputStrategy(options);
exports.logger = (0, cli_framework_output_1.createDefaultLogger)({
output: exports.output,
formatterOptions: {
titleize: false,
tags: new Map([
[cli_framework_output_1.LOGGER_LEVELS.DEBUG, colors_1.default.log.DEBUG('[debug]')],
[cli_framework_output_1.LOGGER_LEVELS.INFO, colors_1.default.log.INFO('[info]')],
[cli_framework_output_1.LOGGER_LEVELS.WARN, colors_1.default.log.WARN('[warn]')],
[cli_framework_output_1.LOGGER_LEVELS.ERROR, colors_1.default.log.ERROR('[error]')],
]),
},
});
async function logPrompt(msg, promptObject) {
const { wordWrap } = await Promise.resolve().then(() => tslib_1.__importStar(require('@ionic/cli-framework-output')));
const { prompt } = await Promise.resolve().then(() => tslib_1.__importStar(require('prompts')));
exports.logger.log({
msg: `${colors_1.default.input('[?]')} ${wordWrap(msg, { indentation: 4 })}`,
logger: exports.logger,
format: false,
});
return prompt(promptObject, { onCancel: () => process.exit(1) });
}
exports.logPrompt = logPrompt;
function logSuccess(msg) {
exports.logger.msg(`${colors_1.default.success('[success]')} ${msg}`);
}
exports.logSuccess = logSuccess;

View file

@ -0,0 +1,186 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getAllElements = exports.getFilePath = exports.getAssets = exports.getJSModules = exports.getPluginType = exports.getPlatformElement = exports.getPluginPlatform = exports.printPlugins = exports.fixName = exports.getDependencies = exports.resolvePlugin = exports.getPlugins = exports.getIncludedPluginPackages = void 0;
const tslib_1 = require("tslib");
const utils_fs_1 = require("@ionic/utils-fs");
const path_1 = require("path");
const colors_1 = tslib_1.__importDefault(require("./colors"));
const errors_1 = require("./errors");
const log_1 = require("./log");
const node_1 = require("./util/node");
const xml_1 = require("./util/xml");
function getIncludedPluginPackages(config, platform) {
var _a, _b, _c, _d;
const { extConfig } = config.app;
switch (platform) {
case 'android':
return (_b = (_a = extConfig.android) === null || _a === void 0 ? void 0 : _a.includePlugins) !== null && _b !== void 0 ? _b : extConfig.includePlugins;
case 'ios':
return (_d = (_c = extConfig.ios) === null || _c === void 0 ? void 0 : _c.includePlugins) !== null && _d !== void 0 ? _d : extConfig.includePlugins;
}
}
exports.getIncludedPluginPackages = getIncludedPluginPackages;
async function getPlugins(config, platform) {
var _a;
const possiblePlugins = (_a = getIncludedPluginPackages(config, platform)) !== null && _a !== void 0 ? _a : getDependencies(config);
const resolvedPlugins = await Promise.all(possiblePlugins.map(async (p) => resolvePlugin(config, p)));
return resolvedPlugins.filter((p) => !!p);
}
exports.getPlugins = getPlugins;
async function resolvePlugin(config, name) {
try {
const packagePath = (0, node_1.resolveNode)(config.app.rootDir, name, 'package.json');
if (!packagePath) {
(0, errors_1.fatal)(`Unable to find ${colors_1.default.strong(`node_modules/${name}`)}.\n` +
`Are you sure ${colors_1.default.strong(name)} is installed?`);
}
const rootPath = (0, path_1.dirname)(packagePath);
const meta = await (0, utils_fs_1.readJSON)(packagePath);
if (!meta) {
return null;
}
if (meta.capacitor) {
return {
id: name,
name: fixName(name),
version: meta.version,
rootPath,
repository: meta.repository,
manifest: meta.capacitor,
};
}
const pluginXMLPath = (0, path_1.join)(rootPath, 'plugin.xml');
const xmlMeta = await (0, xml_1.readXML)(pluginXMLPath);
return {
id: name,
name: fixName(name),
version: meta.version,
rootPath: rootPath,
repository: meta.repository,
xml: xmlMeta.plugin,
};
}
catch (e) {
// ignore
}
return null;
}
exports.resolvePlugin = resolvePlugin;
function getDependencies(config) {
var _a, _b;
return [
...Object.keys((_a = config.app.package.dependencies) !== null && _a !== void 0 ? _a : {}),
...Object.keys((_b = config.app.package.devDependencies) !== null && _b !== void 0 ? _b : {}),
];
}
exports.getDependencies = getDependencies;
function fixName(name) {
name = name
.replace(/\//g, '_')
.replace(/-/g, '_')
.replace(/@/g, '')
.replace(/_\w/g, m => m[1].toUpperCase());
return name.charAt(0).toUpperCase() + name.slice(1);
}
exports.fixName = fixName;
function printPlugins(plugins, platform, type = 'capacitor') {
if (plugins.length === 0) {
return;
}
let msg;
const plural = plugins.length === 1 ? '' : 's';
switch (type) {
case 'cordova':
msg = `Found ${plugins.length} Cordova plugin${plural} for ${colors_1.default.strong(platform)}:\n`;
break;
case 'incompatible':
msg = `Found ${plugins.length} incompatible Cordova plugin${plural} for ${colors_1.default.strong(platform)}, skipped install:\n`;
break;
case 'capacitor':
msg = `Found ${plugins.length} Capacitor plugin${plural} for ${colors_1.default.strong(platform)}:\n`;
break;
}
msg += plugins.map(p => `${p.id}${colors_1.default.weak(`@${p.version}`)}`).join('\n');
log_1.logger.info(msg);
}
exports.printPlugins = printPlugins;
function getPluginPlatform(p, platform) {
const platforms = p.xml.platform;
if (platforms) {
const platforms = p.xml.platform.filter(function (item) {
return item.$.name === platform;
});
return platforms[0];
}
return [];
}
exports.getPluginPlatform = getPluginPlatform;
function getPlatformElement(p, platform, elementName) {
const platformTag = getPluginPlatform(p, platform);
if (platformTag) {
const element = platformTag[elementName];
if (element) {
return element;
}
}
return [];
}
exports.getPlatformElement = getPlatformElement;
function getPluginType(p, platform) {
var _a, _b, _c, _d;
switch (platform) {
case 'ios':
return (_b = (_a = p.ios) === null || _a === void 0 ? void 0 : _a.type) !== null && _b !== void 0 ? _b : 0 /* PluginType.Core */;
case 'android':
return (_d = (_c = p.android) === null || _c === void 0 ? void 0 : _c.type) !== null && _d !== void 0 ? _d : 0 /* PluginType.Core */;
}
return 0 /* PluginType.Core */;
}
exports.getPluginType = getPluginType;
/**
* Get each JavaScript Module for the given plugin
*/
function getJSModules(p, platform) {
return getAllElements(p, platform, 'js-module');
}
exports.getJSModules = getJSModules;
/**
* Get each asset tag for the given plugin
*/
function getAssets(p, platform) {
return getAllElements(p, platform, 'asset');
}
exports.getAssets = getAssets;
function getFilePath(config, plugin, path) {
if (path.startsWith('node_modules')) {
let pathSegments = path.split('/').slice(1);
if (pathSegments[0].startsWith('@')) {
pathSegments = [
pathSegments[0] + '/' + pathSegments[1],
...pathSegments.slice(2),
];
}
const filePath = (0, node_1.resolveNode)(config.app.rootDir, ...pathSegments);
if (!filePath) {
throw new Error(`Can't resolve module ${pathSegments[0]}`);
}
return filePath;
}
return (0, path_1.join)(plugin.rootPath, path);
}
exports.getFilePath = getFilePath;
/**
* For a given plugin, return all the plugin.xml elements with elementName, checking root and specified platform
*/
function getAllElements(p, platform, elementName) {
let modules = [];
if (p.xml[elementName]) {
modules = modules.concat(p.xml[elementName]);
}
const platformModules = getPluginPlatform(p, platform);
if (platformModules === null || platformModules === void 0 ? void 0 : platformModules[elementName]) {
modules = modules.concat(platformModules[elementName]);
}
return modules;
}
exports.getAllElements = getAllElements;

View file

@ -0,0 +1,35 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.writeConfig = exports.readConfig = void 0;
const tslib_1 = require("tslib");
const utils_fs_1 = require("@ionic/utils-fs");
const debug_1 = tslib_1.__importDefault(require("debug"));
const path_1 = require("path");
const cli_1 = require("./util/cli");
const uuid_1 = require("./util/uuid");
const debug = (0, debug_1.default)('capacitor:sysconfig');
const SYSCONFIG_FILE = 'sysconfig.json';
const SYSCONFIG_PATH = (0, path_1.resolve)(cli_1.ENV_PATHS.config, SYSCONFIG_FILE);
async function readConfig() {
debug('Reading from %O', SYSCONFIG_PATH);
try {
return await (0, utils_fs_1.readJSON)(SYSCONFIG_PATH);
}
catch (e) {
if (e.code !== 'ENOENT') {
throw e;
}
const sysconfig = {
machine: (0, uuid_1.uuidv4)(),
};
await writeConfig(sysconfig);
return sysconfig;
}
}
exports.readConfig = readConfig;
async function writeConfig(sysconfig) {
debug('Writing to %O', SYSCONFIG_PATH);
await (0, utils_fs_1.mkdirp)((0, path_1.dirname)(SYSCONFIG_PATH));
await (0, utils_fs_1.writeJSON)(SYSCONFIG_PATH, sysconfig, { spaces: '\t' });
}
exports.writeConfig = writeConfig;

View file

@ -0,0 +1,122 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.addCommand = void 0;
const tslib_1 = require("tslib");
const utils_fs_1 = require("@ionic/utils-fs");
const utils_terminal_1 = require("@ionic/utils-terminal");
const add_1 = require("../android/add");
const common_1 = require("../android/common");
const colors_1 = tslib_1.__importDefault(require("../colors"));
const common_2 = require("../common");
const errors_1 = require("../errors");
const add_2 = require("../ios/add");
const common_3 = require("../ios/common");
const log_1 = require("../log");
const sync_1 = require("./sync");
async function addCommand(config, selectedPlatformName) {
var _a;
if (selectedPlatformName && !(await (0, common_2.isValidPlatform)(selectedPlatformName))) {
const platformDir = (0, common_2.resolvePlatform)(config, selectedPlatformName);
if (platformDir) {
await (0, common_2.runPlatformHook)(config, selectedPlatformName, platformDir, 'capacitor:add');
}
else {
let msg = `Platform ${colors_1.default.input(selectedPlatformName)} not found.`;
if (await (0, common_2.isValidCommunityPlatform)(selectedPlatformName)) {
msg += `\nTry installing ${colors_1.default.strong(`@capacitor-community/${selectedPlatformName}`)} and adding the platform again.`;
}
if (await (0, common_2.isValidEnterprisePlatform)(selectedPlatformName)) {
msg +=
`\nThis is an enterprise platform and @ionic-enterprise/capacitor-${selectedPlatformName} is not installed.\n` +
`To learn how to use this platform, visit https://ionic.io/docs/${selectedPlatformName}`;
}
log_1.logger.error(msg);
}
}
else {
const knownPlatforms = await (0, common_2.getKnownPlatforms)();
const platformName = await (0, common_2.promptForPlatform)(knownPlatforms, `Please choose a platform to add:`, selectedPlatformName);
if (platformName === config.web.name) {
webWarning();
return;
}
const existingPlatformDir = await (0, common_2.getProjectPlatformDirectory)(config, platformName);
if (existingPlatformDir) {
(0, errors_1.fatal)(`${colors_1.default.input(platformName)} platform already exists.\n` +
`To re-add this platform, first remove ${colors_1.default.strong((0, utils_terminal_1.prettyPath)(existingPlatformDir))}, then run this command again.\n` +
`${colors_1.default.strong('WARNING')}: Your native project will be completely removed.`);
}
try {
await (0, common_2.check)([
() => (0, common_2.checkPackage)(),
() => (0, common_2.checkAppConfig)(config),
...addChecks(config, platformName),
]);
await doAdd(config, platformName);
await editPlatforms(config, platformName);
if (await (0, utils_fs_1.pathExists)(config.app.webDirAbs)) {
await (0, sync_1.sync)(config, platformName, false, false);
if (platformName === config.android.name) {
await (0, common_2.runTask)('Syncing Gradle', async () => {
return (0, add_1.createLocalProperties)(config.android.platformDirAbs);
});
}
}
else {
log_1.logger.warn(`${colors_1.default.success(colors_1.default.strong('sync'))} could not run--missing ${colors_1.default.strong(config.app.webDir)} directory.`);
}
printNextSteps(platformName);
}
catch (e) {
if (!(0, errors_1.isFatal)(e)) {
(0, errors_1.fatal)((_a = e.stack) !== null && _a !== void 0 ? _a : e);
}
throw e;
}
}
}
exports.addCommand = addCommand;
function printNextSteps(platformName) {
(0, log_1.logSuccess)(`${colors_1.default.strong(platformName)} platform added!`);
log_1.output.write(`Follow the Developer Workflow guide to get building:\n${colors_1.default.strong(`https://capacitorjs.com/docs/basics/workflow`)}\n`);
}
function addChecks(config, platformName) {
if (platformName === config.ios.name) {
return [
() => (0, common_3.checkIOSPackage)(config),
() => (0, common_3.checkBundler)(config) || (0, common_3.checkCocoaPods)(config),
];
}
else if (platformName === config.android.name) {
return [() => (0, common_1.checkAndroidPackage)(config)];
}
else if (platformName === config.web.name) {
return [];
}
else {
throw `Platform ${platformName} is not valid.`;
}
}
async function doAdd(config, platformName) {
await (0, common_2.runTask)(colors_1.default.success(colors_1.default.strong('add')), async () => {
if (platformName === config.ios.name) {
await (0, add_2.addIOS)(config);
}
else if (platformName === config.android.name) {
await (0, add_1.addAndroid)(config);
}
});
}
async function editPlatforms(config, platformName) {
if (platformName === config.ios.name) {
await (0, common_3.editProjectSettingsIOS)(config);
}
else if (platformName === config.android.name) {
await (0, common_1.editProjectSettingsAndroid)(config);
}
}
function webWarning() {
log_1.logger.error(`Not adding platform ${colors_1.default.strong('web')}.\n` +
`In Capacitor, the web platform is just your web app! For example, if you have a React or Angular project, the web platform is that project.\n` +
`To add Capacitor functionality to your web app, follow the Web Getting Started Guide: ${colors_1.default.strong('https://capacitorjs.com/docs/web')}`);
}

View file

@ -0,0 +1,61 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.build = exports.buildCommand = void 0;
const build_1 = require("../android/build");
const common_1 = require("../common");
const errors_1 = require("../errors");
const build_2 = require("../ios/build");
async function buildCommand(config, selectedPlatformName, buildOptions) {
var _a;
const platforms = await (0, common_1.selectPlatforms)(config, selectedPlatformName);
let platformName;
if (platforms.length === 1) {
platformName = platforms[0];
}
else {
platformName = await (0, common_1.promptForPlatform)(platforms.filter(createBuildablePlatformFilter(config)), `Please choose a platform to build for:`);
}
const buildCommandOptions = {
scheme: buildOptions.scheme || config.ios.scheme,
flavor: buildOptions.flavor || config.android.flavor,
keystorepath: buildOptions.keystorepath || config.android.buildOptions.keystorePath,
keystorepass: buildOptions.keystorepass || config.android.buildOptions.keystorePassword,
keystorealias: buildOptions.keystorealias || config.android.buildOptions.keystoreAlias,
keystorealiaspass: buildOptions.keystorealiaspass ||
config.android.buildOptions.keystoreAliasPassword,
androidreleasetype: buildOptions.androidreleasetype ||
config.android.buildOptions.releaseType ||
'AAB',
signingtype: buildOptions.signingtype ||
config.android.buildOptions.signingType ||
'jarsigner',
};
try {
await build(config, platformName, buildCommandOptions);
}
catch (e) {
if (!(0, errors_1.isFatal)(e)) {
(0, errors_1.fatal)((_a = e.stack) !== null && _a !== void 0 ? _a : e);
}
throw e;
}
}
exports.buildCommand = buildCommand;
async function build(config, platformName, buildOptions) {
if (platformName == config.ios.name) {
await (0, build_2.buildiOS)(config, buildOptions);
}
else if (platformName === config.android.name) {
await (0, build_1.buildAndroid)(config, buildOptions);
}
else if (platformName === config.web.name) {
throw `Platform "${platformName}" is not available in the build command.`;
}
else {
throw `Platform "${platformName}" is not valid.`;
}
}
exports.build = build;
function createBuildablePlatformFilter(config) {
return platform => platform === config.ios.name || platform === config.android.name;
}

View file

@ -0,0 +1,31 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.configCommand = void 0;
const tslib_1 = require("tslib");
const util_1 = tslib_1.__importDefault(require("util"));
const log_1 = require("../log");
async function configCommand(config, json) {
const evaluatedConfig = await deepAwait(config);
if (json) {
process.stdout.write(`${JSON.stringify(evaluatedConfig)}\n`);
}
else {
log_1.output.write(`${util_1.default.inspect(evaluatedConfig, { depth: Infinity, colors: true })}\n`);
}
}
exports.configCommand = configCommand;
async function deepAwait(obj) {
if (obj &&
!Array.isArray(obj) &&
typeof obj === 'object' &&
obj.constructor === Object) {
const o = {};
for (const [k, v] of Object.entries(obj)) {
o[k] = await deepAwait(v);
}
return o;
}
else {
return await obj;
}
}

View file

@ -0,0 +1,212 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.copy = exports.copyCommand = void 0;
const tslib_1 = require("tslib");
const utils_fs_1 = require("@ionic/utils-fs");
const path_1 = require("path");
const colors_1 = tslib_1.__importDefault(require("../colors"));
const common_1 = require("../common");
const cordova_1 = require("../cordova");
const errors_1 = require("../errors");
const log_1 = require("../log");
const plugin_1 = require("../plugin");
const promise_1 = require("../util/promise");
const copy_1 = require("../web/copy");
const sourcemaps_1 = require("./sourcemaps");
async function copyCommand(config, selectedPlatformName, inline = false) {
var _a;
if (selectedPlatformName && !(await (0, common_1.isValidPlatform)(selectedPlatformName))) {
const platformDir = (0, common_1.resolvePlatform)(config, selectedPlatformName);
if (platformDir) {
await (0, common_1.runPlatformHook)(config, selectedPlatformName, platformDir, 'capacitor:copy');
}
else {
log_1.logger.error(`Platform ${colors_1.default.input(selectedPlatformName)} not found.`);
}
}
else {
const platforms = await (0, common_1.selectPlatforms)(config, selectedPlatformName);
try {
await (0, promise_1.allSerial)(platforms.map(platformName => () => copy(config, platformName, inline)));
}
catch (e) {
if ((0, errors_1.isFatal)(e)) {
throw e;
}
log_1.logger.error((_a = e.stack) !== null && _a !== void 0 ? _a : e);
}
}
}
exports.copyCommand = copyCommand;
async function copy(config, platformName, inline = false) {
await (0, common_1.runTask)(colors_1.default.success(colors_1.default.strong(`copy ${platformName}`)), async () => {
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v;
const result = await (0, common_1.checkWebDir)(config);
if (result) {
throw result;
}
await (0, common_1.runPlatformHook)(config, platformName, config.app.rootDir, 'capacitor:copy:before');
const allPlugins = await (0, plugin_1.getPlugins)(config, platformName);
let usesFederatedCapacitor = false;
if (allPlugins.filter(plugin => plugin.id === '@ionic-enterprise/federated-capacitor').length > 0) {
usesFederatedCapacitor = true;
}
let usesLiveUpdates = false;
if (allPlugins.filter(plugin => plugin.id === '@capacitor/live-updates')
.length > 0) {
usesLiveUpdates = true;
}
let usesSSLPinning = false;
if (allPlugins.filter(plugin => plugin.id === '@ionic-enterprise/ssl-pinning')
.length > 0) {
usesSSLPinning = true;
}
if (platformName === config.ios.name) {
if (usesFederatedCapacitor) {
await copyFederatedWebDirs(config, await config.ios.webDirAbs);
if ((_c = (_b = (_a = config.app.extConfig) === null || _a === void 0 ? void 0 : _a.plugins) === null || _b === void 0 ? void 0 : _b.FederatedCapacitor) === null || _c === void 0 ? void 0 : _c.liveUpdatesKey) {
await copySecureLiveUpdatesKey(config.app.extConfig.plugins.FederatedCapacitor.liveUpdatesKey, config.app.rootDir, config.ios.nativeTargetDirAbs);
}
}
else {
await copyWebDir(config, await config.ios.webDirAbs, config.app.webDirAbs);
}
if (usesLiveUpdates && ((_f = (_e = (_d = config.app.extConfig) === null || _d === void 0 ? void 0 : _d.plugins) === null || _e === void 0 ? void 0 : _e.LiveUpdates) === null || _f === void 0 ? void 0 : _f.key)) {
await copySecureLiveUpdatesKey(config.app.extConfig.plugins.LiveUpdates.key, config.app.rootDir, config.ios.nativeTargetDirAbs);
}
if (usesSSLPinning && ((_j = (_h = (_g = config.app.extConfig) === null || _g === void 0 ? void 0 : _g.plugins) === null || _h === void 0 ? void 0 : _h.SSLPinning) === null || _j === void 0 ? void 0 : _j.certs)) {
await copySSLCert((_k = config.app.extConfig.plugins.SSLPinning) === null || _k === void 0 ? void 0 : _k.certs, config.app.rootDir, await config.ios.webDirAbs);
}
await copyCapacitorConfig(config, config.ios.nativeTargetDirAbs);
const cordovaPlugins = await (0, cordova_1.getCordovaPlugins)(config, platformName);
await (0, cordova_1.handleCordovaPluginsJS)(cordovaPlugins, config, platformName);
}
else if (platformName === config.android.name) {
if (usesFederatedCapacitor) {
await copyFederatedWebDirs(config, config.android.webDirAbs);
if ((_o = (_m = (_l = config.app.extConfig) === null || _l === void 0 ? void 0 : _l.plugins) === null || _m === void 0 ? void 0 : _m.FederatedCapacitor) === null || _o === void 0 ? void 0 : _o.liveUpdatesKey) {
await copySecureLiveUpdatesKey(config.app.extConfig.plugins.FederatedCapacitor.liveUpdatesKey, config.app.rootDir, config.android.assetsDirAbs);
}
}
else {
await copyWebDir(config, config.android.webDirAbs, config.app.webDirAbs);
}
if (usesLiveUpdates && ((_r = (_q = (_p = config.app.extConfig) === null || _p === void 0 ? void 0 : _p.plugins) === null || _q === void 0 ? void 0 : _q.LiveUpdates) === null || _r === void 0 ? void 0 : _r.key)) {
await copySecureLiveUpdatesKey(config.app.extConfig.plugins.LiveUpdates.key, config.app.rootDir, config.android.assetsDirAbs);
}
if (usesSSLPinning && ((_u = (_t = (_s = config.app.extConfig) === null || _s === void 0 ? void 0 : _s.plugins) === null || _t === void 0 ? void 0 : _t.SSLPinning) === null || _u === void 0 ? void 0 : _u.certs)) {
await copySSLCert((_v = config.app.extConfig.plugins.SSLPinning) === null || _v === void 0 ? void 0 : _v.certs, config.app.rootDir, config.android.assetsDirAbs);
}
await copyCapacitorConfig(config, config.android.assetsDirAbs);
const cordovaPlugins = await (0, cordova_1.getCordovaPlugins)(config, platformName);
await (0, cordova_1.handleCordovaPluginsJS)(cordovaPlugins, config, platformName);
await (0, cordova_1.writeCordovaAndroidManifest)(cordovaPlugins, config, platformName);
}
else if (platformName === config.web.name) {
if (usesFederatedCapacitor) {
log_1.logger.info('FederatedCapacitor Plugin installed, skipping web bundling...');
}
else {
await (0, copy_1.copyWeb)(config);
}
}
else {
throw `Platform ${platformName} is not valid.`;
}
if (inline) {
await (0, sourcemaps_1.inlineSourceMaps)(config, platformName);
}
});
await (0, common_1.runPlatformHook)(config, platformName, config.app.rootDir, 'capacitor:copy:after');
}
exports.copy = copy;
async function copyCapacitorConfig(config, nativeAbsDir) {
const nativeRelDir = (0, path_1.relative)(config.app.rootDir, nativeAbsDir);
const nativeConfigFile = 'capacitor.config.json';
const nativeConfigFilePath = (0, path_1.join)(nativeAbsDir, nativeConfigFile);
await (0, common_1.runTask)(`Creating ${colors_1.default.strong(nativeConfigFile)} in ${nativeRelDir}`, async () => {
var _a;
(_a = config.app.extConfig.android) === null || _a === void 0 ? true : delete _a.buildOptions;
await (0, utils_fs_1.writeJSON)(nativeConfigFilePath, config.app.extConfig, {
spaces: '\t',
});
});
}
async function copyWebDir(config, nativeAbsDir, webAbsDir) {
var _a;
const webRelDir = (0, path_1.basename)(webAbsDir);
const nativeRelDir = (0, path_1.relative)(config.app.rootDir, nativeAbsDir);
if (((_a = config.app.extConfig.server) === null || _a === void 0 ? void 0 : _a.url) && !(await (0, utils_fs_1.pathExists)(webAbsDir))) {
log_1.logger.warn(`Cannot copy web assets from ${colors_1.default.strong(webRelDir)} to ${nativeRelDir}\n` +
`Web asset directory specified by ${colors_1.default.input('webDir')} does not exist. This is not an error because ${colors_1.default.input('server.url')} is set in config.`);
return;
}
await (0, common_1.runTask)(`Copying web assets from ${colors_1.default.strong(webRelDir)} to ${nativeRelDir}`, async () => {
await (0, utils_fs_1.remove)(nativeAbsDir);
return (0, utils_fs_1.copy)(webAbsDir, nativeAbsDir);
});
}
async function copyFederatedWebDirs(config, nativeAbsDir) {
var _a, _b;
log_1.logger.info('FederatedCapacitor Plugin Loaded - Copying Web Assets');
if (!((_b = (_a = config.app.extConfig) === null || _a === void 0 ? void 0 : _a.plugins) === null || _b === void 0 ? void 0 : _b.FederatedCapacitor)) {
throw `FederatedCapacitor plugin is present but no valid config is defined.`;
}
const federatedConfig = config.app.extConfig.plugins.FederatedCapacitor;
if (federatedConfig) {
if (federatedConfig.shell.name === undefined) {
throw `FederatedCapacitor plugin is present but no valid Shell application is defined in the config.`;
}
if (!federatedConfig.apps.every(isFederatedApp)) {
throw `FederatedCapacitor plugin is present but there is a problem with the apps defined in the config.`;
}
const copyApps = () => {
return federatedConfig.apps.map(app => {
const appDir = (0, path_1.resolve)(config.app.rootDir, app.webDir);
return copyWebDir(config, (0, path_1.resolve)(nativeAbsDir, app.name), appDir);
});
};
const copyShell = () => {
return copyWebDir(config, (0, path_1.resolve)(nativeAbsDir, federatedConfig.shell.name), config.app.webDirAbs);
};
await Promise.all([...copyApps(), copyShell()]);
}
}
function isFederatedApp(config) {
return (config.webDir !== undefined &&
config.name !== undefined);
}
async function copySecureLiveUpdatesKey(secureLiveUpdatesKeyFile, rootDir, nativeAbsDir) {
const keyAbsFromPath = (0, path_1.join)(rootDir, secureLiveUpdatesKeyFile);
const keyAbsToPath = (0, path_1.join)(nativeAbsDir, (0, path_1.basename)(keyAbsFromPath));
const keyRelToDir = (0, path_1.relative)(rootDir, nativeAbsDir);
if (!(await (0, utils_fs_1.pathExists)(keyAbsFromPath))) {
log_1.logger.warn(`Cannot copy Secure Live Updates signature file from ${colors_1.default.strong(keyAbsFromPath)} to ${keyRelToDir}\n` +
`Signature file does not exist at specified key path.`);
return;
}
await (0, common_1.runTask)(`Copying Secure Live Updates key from ${colors_1.default.strong(secureLiveUpdatesKeyFile)} to ${keyRelToDir}`, async () => {
return (0, utils_fs_1.copy)(keyAbsFromPath, keyAbsToPath);
});
}
async function copySSLCert(sslCertPaths, rootDir, targetDir) {
const validCertPaths = [];
for (const sslCertPath of sslCertPaths) {
const certAbsFromPath = (0, path_1.join)(rootDir, sslCertPath);
if (!(await (0, utils_fs_1.pathExists)(certAbsFromPath))) {
log_1.logger.warn(`Cannot copy SSL Certificate file from ${colors_1.default.strong(certAbsFromPath)}\n` +
`SSL Certificate does not exist at specified path.`);
return;
}
validCertPaths.push(certAbsFromPath);
}
const certsDirAbsToPath = (0, path_1.join)(targetDir, 'certs');
const certsDirRelToDir = (0, path_1.relative)(rootDir, targetDir);
await (0, common_1.runTask)(`Copying SSL Certificates from to ${certsDirRelToDir}`, async () => {
const promises = [];
for (const certPath of validCertPaths) {
promises.push((0, utils_fs_1.copy)(certPath, (0, path_1.join)(certsDirAbsToPath, (0, path_1.basename)(certPath))));
}
return Promise.all(promises);
});
}

View file

@ -0,0 +1,11 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.createCommand = void 0;
const tslib_1 = require("tslib");
const colors_1 = tslib_1.__importDefault(require("../colors"));
const errors_1 = require("../errors");
async function createCommand() {
(0, errors_1.fatal)(`The create command has been removed.\n` +
`Use ${colors_1.default.input('npm init @capacitor/app')}`);
}
exports.createCommand = createCommand;

View file

@ -0,0 +1,73 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.doctor = exports.doctorCore = exports.doctorCommand = void 0;
const tslib_1 = require("tslib");
const utils_fs_1 = require("@ionic/utils-fs");
const doctor_1 = require("../android/doctor");
const colors_1 = tslib_1.__importDefault(require("../colors"));
const common_1 = require("../common");
const doctor_2 = require("../ios/doctor");
const log_1 = require("../log");
const emoji_1 = require("../util/emoji");
const node_1 = require("../util/node");
const subprocess_1 = require("../util/subprocess");
async function doctorCommand(config, selectedPlatformName) {
log_1.output.write(`${(0, emoji_1.emoji)('💊', '')} ${colors_1.default.strong('Capacitor Doctor')} ${(0, emoji_1.emoji)('💊', '')} \n\n`);
await doctorCore(config);
const platforms = await (0, common_1.selectPlatforms)(config, selectedPlatformName);
await Promise.all(platforms.map(platformName => {
return doctor(config, platformName);
}));
}
exports.doctorCommand = doctorCommand;
async function doctorCore(config) {
const [cliVersion, coreVersion, androidVersion, iosVersion] = await Promise.all([
(0, subprocess_1.getCommandOutput)('npm', ['info', '@capacitor/cli', 'version']),
(0, subprocess_1.getCommandOutput)('npm', ['info', '@capacitor/core', 'version']),
(0, subprocess_1.getCommandOutput)('npm', ['info', '@capacitor/android', 'version']),
(0, subprocess_1.getCommandOutput)('npm', ['info', '@capacitor/ios', 'version']),
]);
log_1.output.write(`${colors_1.default.strong('Latest Dependencies:')}\n\n` +
` @capacitor/cli: ${colors_1.default.weak(cliVersion !== null && cliVersion !== void 0 ? cliVersion : 'unknown')}\n` +
` @capacitor/core: ${colors_1.default.weak(coreVersion !== null && coreVersion !== void 0 ? coreVersion : 'unknown')}\n` +
` @capacitor/android: ${colors_1.default.weak(androidVersion !== null && androidVersion !== void 0 ? androidVersion : 'unknown')}\n` +
` @capacitor/ios: ${colors_1.default.weak(iosVersion !== null && iosVersion !== void 0 ? iosVersion : 'unknown')}\n\n` +
`${colors_1.default.strong('Installed Dependencies:')}\n\n`);
await printInstalledPackages(config);
log_1.output.write('\n');
}
exports.doctorCore = doctorCore;
async function printInstalledPackages(config) {
const packageNames = [
'@capacitor/cli',
'@capacitor/core',
'@capacitor/android',
'@capacitor/ios',
];
await Promise.all(packageNames.map(async (packageName) => {
const packagePath = (0, node_1.resolveNode)(config.app.rootDir, packageName, 'package.json');
await printPackageVersion(packageName, packagePath);
}));
}
async function printPackageVersion(packageName, packagePath) {
let version;
if (packagePath) {
version = (await (0, utils_fs_1.readJSON)(packagePath)).version;
}
log_1.output.write(` ${packageName}: ${colors_1.default.weak(version || 'not installed')}\n`);
}
async function doctor(config, platformName) {
if (platformName === config.ios.name) {
await (0, doctor_2.doctorIOS)(config);
}
else if (platformName === config.android.name) {
await (0, doctor_1.doctorAndroid)(config);
}
else if (platformName === config.web.name) {
return Promise.resolve();
}
else {
throw `Platform ${platformName} is not valid.`;
}
}
exports.doctor = doctor;

View file

@ -0,0 +1,143 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.initCommand = void 0;
const tslib_1 = require("tslib");
const open_1 = tslib_1.__importDefault(require("open"));
const path_1 = require("path");
const colors_1 = tslib_1.__importDefault(require("../colors"));
const common_1 = require("../common");
const config_1 = require("../config");
const cordova_1 = require("../cordova");
const errors_1 = require("../errors");
const framework_configs_1 = require("../framework-configs");
const log_1 = require("../log");
const sysconfig_1 = require("../sysconfig");
const node_1 = require("../util/node");
const term_1 = require("../util/term");
async function initCommand(config, name, id, webDirFromCLI) {
var _a, _b, _c, _d;
try {
if (!(0, term_1.checkInteractive)(name, id)) {
return;
}
if (config.app.extConfigType !== 'json') {
(0, errors_1.fatal)(`Cannot run ${colors_1.default.input('init')} for a project using a non-JSON configuration file.\n` +
`Delete ${colors_1.default.strong(config.app.extConfigName)} and try again.`);
}
const isNewConfig = Object.keys(config.app.extConfig).length === 0;
const tsInstalled = !!(0, node_1.resolveNode)(config.app.rootDir, 'typescript');
const appName = await getName(config, name);
const appId = await getAppId(config, id);
const webDir = (0, term_1.isInteractive)()
? await getWebDir(config, webDirFromCLI)
: (_a = webDirFromCLI !== null && webDirFromCLI !== void 0 ? webDirFromCLI : config.app.extConfig.webDir) !== null && _a !== void 0 ? _a : 'www';
await (0, common_1.check)([
() => (0, common_1.checkAppName)(config, appName),
() => (0, common_1.checkAppId)(config, appId),
]);
const androidScheme = (_c = (_b = config.app.extConfig.server) === null || _b === void 0 ? void 0 : _b.androidScheme) !== null && _c !== void 0 ? _c : 'https';
const cordova = await (0, cordova_1.getCordovaPreferences)(config);
await runMergeConfig(config, {
appId,
appName,
webDir,
server: {
androidScheme: androidScheme,
},
cordova,
}, isNewConfig && tsInstalled ? 'ts' : 'json');
}
catch (e) {
if (!(0, errors_1.isFatal)(e)) {
log_1.output.write('Usage: npx cap init appName appId\n' +
'Example: npx cap init "My App" "com.example.myapp"\n\n');
(0, errors_1.fatal)((_d = e.stack) !== null && _d !== void 0 ? _d : e);
}
throw e;
}
}
exports.initCommand = initCommand;
async function getName(config, name) {
var _a;
if (!name) {
const answers = await (0, log_1.logPrompt)(`${colors_1.default.strong(`What is the name of your app?`)}\n` +
`This should be a human-friendly app name, like what you'd see in the App Store.`, {
type: 'text',
name: 'name',
message: `Name`,
initial: config.app.appName
? config.app.appName
: (_a = config.app.package.name) !== null && _a !== void 0 ? _a : 'App',
});
return answers.name;
}
return name;
}
async function getAppId(config, id) {
if (!id) {
const answers = await (0, log_1.logPrompt)(`${colors_1.default.strong(`What should be the Package ID for your app?`)}\n` +
`Package IDs (aka Bundle ID in iOS and Application ID in Android) are unique identifiers for apps. They must be in reverse domain name notation, generally representing a domain name that you or your company owns.`, {
type: 'text',
name: 'id',
message: `Package ID`,
initial: config.app.appId ? config.app.appId : 'com.example.app',
});
return answers.id;
}
return id;
}
async function getWebDir(config, webDir) {
if (!webDir) {
const framework = (0, framework_configs_1.detectFramework)(config);
if (framework === null || framework === void 0 ? void 0 : framework.webDir) {
return framework.webDir;
}
const answers = await (0, log_1.logPrompt)(`${colors_1.default.strong(`What is the web asset directory for your app?`)}\n` +
`This directory should contain the final ${colors_1.default.strong('index.html')} of your app.`, {
type: 'text',
name: 'webDir',
message: `Web asset directory`,
initial: config.app.webDir ? config.app.webDir : 'www',
});
return answers.webDir;
}
return webDir;
}
async function runMergeConfig(config, extConfig, type) {
const configDirectory = (0, path_1.dirname)(config.app.extConfigFilePath);
const newConfigPath = (0, path_1.resolve)(configDirectory, type === 'ts' ? config_1.CONFIG_FILE_NAME_TS : config_1.CONFIG_FILE_NAME_JSON);
await (0, common_1.runTask)(`Creating ${colors_1.default.strong((0, path_1.basename)(newConfigPath))} in ${colors_1.default.input(config.app.rootDir)}`, async () => {
await mergeConfig(config, extConfig, newConfigPath);
});
printNextSteps((0, path_1.basename)(newConfigPath));
if ((0, term_1.isInteractive)()) {
let sysconfig = await (0, sysconfig_1.readConfig)();
if (typeof sysconfig.signup === 'undefined') {
const signup = await promptToSignup();
sysconfig = { ...sysconfig, signup };
await (0, sysconfig_1.writeConfig)(sysconfig);
}
}
}
async function mergeConfig(config, extConfig, newConfigPath) {
const oldConfig = { ...config.app.extConfig };
const newConfig = { ...oldConfig, ...extConfig };
await (0, config_1.writeConfig)(newConfig, newConfigPath);
}
function printNextSteps(newConfigName) {
(0, log_1.logSuccess)(`${colors_1.default.strong(newConfigName)} created!`);
log_1.output.write(`\nNext steps: \n${colors_1.default.strong(`https://capacitorjs.com/docs/getting-started#where-to-go-next`)}\n`);
}
async function promptToSignup() {
const answers = await (0, log_1.logPrompt)(`Join the Ionic Community! 💙\n` +
`Connect with millions of developers on the Ionic Forum and get access to live events, news updates, and more.`, {
type: 'confirm',
name: 'create',
message: `Create free Ionic account?`,
initial: true,
});
if (answers.create) {
(0, open_1.default)(`http://ionicframework.com/signup?source=capacitor`);
}
return answers.create;
}

View file

@ -0,0 +1,50 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.list = exports.listCommand = void 0;
const tslib_1 = require("tslib");
const common_1 = require("../android/common");
const colors_1 = tslib_1.__importDefault(require("../colors"));
const common_2 = require("../common");
const errors_1 = require("../errors");
const common_3 = require("../ios/common");
const log_1 = require("../log");
const plugin_1 = require("../plugin");
const promise_1 = require("../util/promise");
async function listCommand(config, selectedPlatformName) {
var _a;
const platforms = await (0, common_2.selectPlatforms)(config, selectedPlatformName);
try {
await (0, promise_1.allSerial)(platforms.map(platformName => () => list(config, platformName)));
}
catch (e) {
if ((0, errors_1.isFatal)(e)) {
throw e;
}
log_1.logger.error((_a = e.stack) !== null && _a !== void 0 ? _a : e);
}
}
exports.listCommand = listCommand;
async function list(config, platform) {
const allPlugins = await (0, plugin_1.getPlugins)(config, platform);
let plugins = [];
if (platform === config.ios.name) {
plugins = await (0, common_3.getIOSPlugins)(allPlugins);
}
else if (platform === config.android.name) {
plugins = await (0, common_1.getAndroidPlugins)(allPlugins);
}
else if (platform === config.web.name) {
log_1.logger.info(`Listing plugins for ${colors_1.default.input(platform)} is not possible.`);
return;
}
else {
throw `Platform ${colors_1.default.input(platform)} is not valid.`;
}
const capacitorPlugins = plugins.filter(p => (0, plugin_1.getPluginType)(p, platform) === 0 /* PluginType.Core */);
(0, plugin_1.printPlugins)(capacitorPlugins, platform);
const cordovaPlugins = plugins.filter(p => (0, plugin_1.getPluginType)(p, platform) === 1 /* PluginType.Cordova */);
(0, plugin_1.printPlugins)(cordovaPlugins, platform, 'cordova');
const incompatibleCordovaPlugins = plugins.filter(p => (0, plugin_1.getPluginType)(p, platform) === 2 /* PluginType.Incompatible */);
(0, plugin_1.printPlugins)(incompatibleCordovaPlugins, platform, 'incompatible');
}
exports.list = list;

View file

@ -0,0 +1,611 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.patchOldCapacitorPlugins = exports.migrateCommand = void 0;
const tslib_1 = require("tslib");
const utils_fs_1 = require("@ionic/utils-fs");
const path_1 = require("path");
const rimraf_1 = tslib_1.__importDefault(require("rimraf"));
const common_1 = require("../android/common");
const colors_1 = tslib_1.__importDefault(require("../colors"));
const common_2 = require("../common");
const errors_1 = require("../errors");
const log_1 = require("../log");
const plugin_1 = require("../plugin");
const fs_1 = require("../util/fs");
const node_1 = require("../util/node");
const subprocess_1 = require("../util/subprocess");
const template_1 = require("../util/template");
// eslint-disable-next-line prefer-const
let allDependencies = {};
const libs = [
'@capacitor/core',
'@capacitor/cli',
'@capacitor/ios',
'@capacitor/android',
];
const plugins = [
'@capacitor/action-sheet',
'@capacitor/app',
'@capacitor/app-launcher',
'@capacitor/browser',
'@capacitor/camera',
'@capacitor/clipboard',
'@capacitor/device',
'@capacitor/dialog',
'@capacitor/filesystem',
'@capacitor/geolocation',
'@capacitor/google-maps',
'@capacitor/haptics',
'@capacitor/keyboard',
'@capacitor/local-notifications',
'@capacitor/motion',
'@capacitor/network',
'@capacitor/preferences',
'@capacitor/push-notifications',
'@capacitor/screen-reader',
'@capacitor/screen-orientation',
'@capacitor/share',
'@capacitor/splash-screen',
'@capacitor/status-bar',
'@capacitor/text-zoom',
'@capacitor/toast',
];
const coreVersion = '^5.0.0';
const pluginVersion = '^5.0.0';
const gradleVersion = '8.0.2';
let installFailed = false;
async function migrateCommand(config, noprompt, packagemanager) {
if (config === null) {
(0, errors_1.fatal)('Config data missing');
}
const capMajor = await checkCapacitorMajorVersion(config);
if (capMajor < 4) {
(0, errors_1.fatal)('Migrate can only be used on capacitor 4 and above, please use the CLI in Capacitor 4 to upgrade to 4 first');
}
const jdkMajor = await (0, common_2.checkJDKMajorVersion)();
if (jdkMajor < 17) {
log_1.logger.warn('Capacitor 5 requires JDK 17 or higher. Some steps may fail.');
}
const variablesAndClasspaths = await getAndroidVariablesAndClasspaths(config);
if (!variablesAndClasspaths) {
(0, errors_1.fatal)('Variable and Classpath info could not be read.');
}
allDependencies = {
...config.app.package.dependencies,
...config.app.package.devDependencies,
};
const monorepoWarning = 'Please note this tool is not intended for use in a mono-repo enviroment, please check out the Ionic vscode extension for this functionality.';
log_1.logger.info(monorepoWarning);
const { migrateconfirm } = noprompt
? { migrateconfirm: 'y' }
: await (0, log_1.logPrompt)(`Capacitor 5 sets a deployment target of iOS 13 and Android 13 (SDK 33). \n`, {
type: 'text',
name: 'migrateconfirm',
message: `Are you sure you want to migrate? (Y/n)`,
initial: 'y',
});
if (typeof migrateconfirm === 'string' &&
migrateconfirm.toLowerCase() === 'y') {
try {
const { depInstallConfirm } = noprompt
? { depInstallConfirm: 'y' }
: await (0, log_1.logPrompt)(`Would you like the migrator to run npm, yarn, or pnpm install to install the latest versions of capacitor packages? (Those using other package managers should answer N)`, {
type: 'text',
name: 'depInstallConfirm',
message: `Run Dependency Install? (Y/n)`,
initial: 'y',
});
const runNpmInstall = typeof depInstallConfirm === 'string' &&
depInstallConfirm.toLowerCase() === 'y';
let installerType = 'npm';
if (runNpmInstall) {
const { manager } = packagemanager
? { manager: packagemanager }
: await (0, log_1.logPrompt)('What dependency manager do you use?', {
type: 'select',
name: 'manager',
message: `Dependency Management Tool`,
choices: [
{ title: 'NPM', value: 'npm' },
{ title: 'Yarn', value: 'yarn' },
{ title: 'PNPM', value: 'pnpm' },
],
initial: 0,
});
installerType = manager;
}
try {
await (0, common_2.runTask)(`Installing Latest Modules using ${installerType}.`, () => {
return installLatestLibs(installerType, runNpmInstall, config);
});
}
catch (ex) {
log_1.logger.error(`${installerType} install failed. Try deleting node_modules folder and running ${colors_1.default.input(`${installerType} install --force`)} manually.`);
installFailed = true;
}
// Update iOS Projects
if (allDependencies['@capacitor/ios'] &&
(0, utils_fs_1.existsSync)(config.ios.platformDirAbs)) {
//Update icon to single 1024 x 1024 icon
await (0, common_2.runTask)('Update App Icon to only 1024 x 1024', () => {
return updateAppIcons(config);
});
//Remove Podfile.lock from .gitignore
await (0, common_2.runTask)('Remove Podfile.lock from iOS .gitignore', () => {
return updateIosGitIgnore((0, path_1.join)(config.ios.platformDirAbs, '.gitignore'));
});
}
if (allDependencies['@capacitor/android'] &&
(0, utils_fs_1.existsSync)(config.android.platformDirAbs)) {
await (0, common_2.runTask)(`Migrating build.gradle file.`, () => {
return updateBuildGradle((0, path_1.join)(config.android.platformDirAbs, 'build.gradle'), variablesAndClasspaths);
});
// Remove enableJetifier
await (0, common_2.runTask)('Remove android.enableJetifier=true from gradle.properties', () => {
return updateGradleProperties((0, path_1.join)(config.android.platformDirAbs, 'gradle.properties'));
});
// Move package from android manifest
await (0, common_2.runTask)('Migrating package from Manifest to build.gradle', () => {
return movePackageFromManifestToBuildGradle((0, path_1.join)(config.android.platformDirAbs, 'app', 'src', 'main', 'AndroidManifest.xml'), (0, path_1.join)(config.android.platformDirAbs, 'app', 'build.gradle'));
});
// Update gradle-wrapper.properties
await (0, common_2.runTask)(`Migrating gradle-wrapper.properties by updating gradle version to ${gradleVersion}.`, () => {
return updateGradleWrapper((0, path_1.join)(config.android.platformDirAbs, 'gradle', 'wrapper', 'gradle-wrapper.properties'));
});
// Variables gradle
await (0, common_2.runTask)(`Migrating variables.gradle file.`, () => {
return (async () => {
const variablesPath = (0, path_1.join)(config.android.platformDirAbs, 'variables.gradle');
let txt = readFile(variablesPath);
if (!txt) {
return;
}
txt = txt.replace(/= {2}'/g, `= '`);
(0, utils_fs_1.writeFileSync)(variablesPath, txt, { encoding: 'utf-8' });
for (const variable of Object.keys(variablesAndClasspaths.variables)) {
if (!(await updateFile(config, variablesPath, `${variable} = '`, `'`, variablesAndClasspaths.variables[variable].toString(), true))) {
const didWork = await updateFile(config, variablesPath, `${variable} = `, `\n`, variablesAndClasspaths.variables[variable].toString(), true);
if (!didWork) {
let file = readFile(variablesPath);
if (file) {
file = file.replace('}', ` ${variable} = '${variablesAndClasspaths.variables[variable].toString()}'\n}`);
(0, utils_fs_1.writeFileSync)(variablesPath, file);
}
}
}
}
const pluginVariables = {
firebaseMessagingVersion: '23.1.2',
playServicesLocationVersion: '21.0.1',
androidxBrowserVersion: '1.5.0',
androidxMaterialVersion: '1.8.0',
androidxExifInterfaceVersion: '1.3.6',
androidxCoreKTXVersion: '1.10.0',
googleMapsPlayServicesVersion: '18.1.0',
googleMapsUtilsVersion: '3.4.0',
googleMapsKtxVersion: '3.4.0',
googleMapsUtilsKtxVersion: '3.4.0',
kotlinxCoroutinesVersion: '1.6.4',
};
for (const variable of Object.keys(pluginVariables)) {
await updateFile(config, variablesPath, `${variable} = '`, `'`, pluginVariables[variable], true);
}
})();
});
rimraf_1.default.sync((0, path_1.join)(config.android.appDirAbs, 'build'));
if (!installFailed) {
await (0, common_2.runTask)('Migrating package from Manifest to build.gradle in Capacitor plugins', () => {
return patchOldCapacitorPlugins(config);
});
}
else {
log_1.logger.warn('Skipped migrating package from Manifest to build.gradle in Capacitor plugins');
}
}
if (!installFailed) {
// Run Cap Sync
await (0, common_2.runTask)(`Running cap sync.`, () => {
return (0, subprocess_1.getCommandOutput)('npx', ['cap', 'sync']);
});
}
else {
log_1.logger.warn('Skipped Running cap sync.');
}
if (allDependencies['@capacitor/android'] &&
(0, utils_fs_1.existsSync)(config.android.platformDirAbs)) {
if (!installFailed) {
try {
await (0, common_2.runTask)(`Upgrading gradle wrapper files`, () => {
return updateGradleWrapperFiles(config.android.platformDirAbs);
});
}
catch (e) {
if (e.includes('EACCES')) {
log_1.logger.error(`gradlew file does not have executable permissions. This can happen if the Android platform was added on a Windows machine. Please run ${colors_1.default.input(`chmod +x ./${config.android.platformDir}/gradlew`)} and ${colors_1.default.input(`cd ${config.android.platformDir} && ./gradlew wrapper --distribution-type all --gradle-version ${gradleVersion} --warning-mode all`)} to update the files manually`);
}
else {
log_1.logger.error(`gradle wrapper files were not updated`);
}
}
}
else {
log_1.logger.warn('Skipped upgrading gradle wrapper files');
}
}
// Write all breaking changes
await (0, common_2.runTask)(`Writing breaking changes.`, () => {
return writeBreakingChanges();
});
if (!installFailed) {
(0, log_1.logSuccess)(`Migration to Capacitor ${coreVersion} is complete. Run and test your app!`);
}
else {
log_1.logger.warn(`Migration to Capacitor ${coreVersion} is incomplete. Check the log messages for more information.`);
}
}
catch (err) {
(0, errors_1.fatal)(`Failed to migrate: ${err}`);
}
}
else {
(0, errors_1.fatal)(`User canceled migration.`);
}
}
exports.migrateCommand = migrateCommand;
async function checkCapacitorMajorVersion(config) {
var _a;
const capacitorVersion = await (0, common_2.getCoreVersion)(config);
const versionArray = (_a = capacitorVersion.match(/([0-9]+)\.([0-9]+)\.([0-9]+)/)) !== null && _a !== void 0 ? _a : [];
const majorVersion = parseInt(versionArray[1]);
return majorVersion;
}
async function installLatestLibs(dependencyManager, runInstall, config) {
const pkgJsonPath = (0, path_1.join)(config.app.rootDir, 'package.json');
const pkgJsonFile = readFile(pkgJsonPath);
if (!pkgJsonFile) {
return;
}
const pkgJson = JSON.parse(pkgJsonFile);
for (const devDepKey of Object.keys(pkgJson['devDependencies'] || {})) {
if (libs.includes(devDepKey)) {
pkgJson['devDependencies'][devDepKey] = coreVersion;
}
else if (plugins.includes(devDepKey)) {
pkgJson['devDependencies'][devDepKey] = pluginVersion;
}
}
for (const depKey of Object.keys(pkgJson['dependencies'] || {})) {
if (libs.includes(depKey)) {
pkgJson['dependencies'][depKey] = coreVersion;
}
else if (plugins.includes(depKey)) {
pkgJson['dependencies'][depKey] = pluginVersion;
}
}
(0, utils_fs_1.writeFileSync)(pkgJsonPath, JSON.stringify(pkgJson, null, 2), {
encoding: 'utf-8',
});
if (runInstall) {
rimraf_1.default.sync((0, path_1.join)(config.app.rootDir, 'node_modules/@capacitor/!(cli)'));
await (0, subprocess_1.runCommand)(dependencyManager, ['install']);
if (dependencyManager == 'yarn') {
await (0, subprocess_1.runCommand)(dependencyManager, ['upgrade']);
}
else {
await (0, subprocess_1.runCommand)(dependencyManager, ['update']);
}
}
else {
log_1.logger.info(`Please run an install command with your package manager of choice. (ex: yarn install)`);
}
}
async function writeBreakingChanges() {
const breaking = [
'@capacitor/camera',
'@capacitor/device',
'@capacitor/local-notifications',
'@capacitor/push-notifications',
];
const broken = [];
for (const lib of breaking) {
if (allDependencies[lib]) {
broken.push(lib);
}
}
if (broken.length > 0) {
log_1.logger.info(`IMPORTANT: Review https://capacitorjs.com/docs/updating/5-0#plugins for breaking changes in these plugins that you use: ${broken.join(', ')}.`);
}
}
async function getAndroidVariablesAndClasspaths(config) {
const tempAndroidTemplateFolder = (0, path_1.join)(config.cli.assetsDirAbs, 'tempAndroidTemplate');
await (0, template_1.extractTemplate)(config.cli.assets.android.platformTemplateArchiveAbs, tempAndroidTemplateFolder);
const variablesGradleFile = readFile((0, path_1.join)(tempAndroidTemplateFolder, 'variables.gradle'));
const buildGradleFile = readFile((0, path_1.join)(tempAndroidTemplateFolder, 'build.gradle'));
if (!variablesGradleFile || !buildGradleFile) {
return;
}
(0, fs_1.deleteFolderRecursive)(tempAndroidTemplateFolder);
const firstIndxOfCATBGV = buildGradleFile.indexOf(`classpath 'com.android.tools.build:gradle:`) + 42;
const firstIndxOfCGGGS = buildGradleFile.indexOf(`com.google.gms:google-services:`) + 31;
const comAndroidToolsBuildGradleVersion = '' +
buildGradleFile.substring(firstIndxOfCATBGV, buildGradleFile.indexOf("'", firstIndxOfCATBGV));
const comGoogleGmsGoogleServices = '' +
buildGradleFile.substring(firstIndxOfCGGGS, buildGradleFile.indexOf("'", firstIndxOfCGGGS));
const variablesGradleAsJSON = JSON.parse(variablesGradleFile
.replace('ext ', '')
.replace(/=/g, ':')
.replace(/\n/g, ',')
.replace(/,([^:]+):/g, function (_k, p1) {
return `,"${p1}":`;
})
.replace('{,', '{')
.replace(',}', '}')
.replace(/\s/g, '')
.replace(/'/g, '"'));
return {
'variables': variablesGradleAsJSON,
'com.android.tools.build:gradle': comAndroidToolsBuildGradleVersion,
'com.google.gms:google-services': comGoogleGmsGoogleServices,
};
}
function readFile(filename) {
try {
if (!(0, utils_fs_1.existsSync)(filename)) {
log_1.logger.error(`Unable to find ${filename}. Try updating it manually`);
return;
}
return (0, utils_fs_1.readFileSync)(filename, 'utf-8');
}
catch (err) {
log_1.logger.error(`Unable to read ${filename}. Verify it is not already open. ${err}`);
}
}
async function updateGradleWrapper(filename) {
const txt = readFile(filename);
if (!txt) {
return;
}
const replaced = setAllStringIn(txt, 'distributionUrl=', '\n',
// eslint-disable-next-line no-useless-escape
`https\\://services.gradle.org/distributions/gradle-${gradleVersion}-all.zip`);
(0, utils_fs_1.writeFileSync)(filename, replaced, 'utf-8');
}
async function updateGradleWrapperFiles(platformDir) {
await (0, subprocess_1.runCommand)(`./gradlew`, [
'wrapper',
'--distribution-type',
'all',
'--gradle-version',
gradleVersion,
'--warning-mode',
'all',
], {
cwd: platformDir,
});
}
async function updateIosGitIgnore(filename) {
const txt = readFile(filename);
if (!txt) {
return;
}
const lines = txt.split('\n');
let linesToKeep = '';
for (const line of lines) {
// check for enableJetifier
const podfileMatch = line.match(/.+Podfile\.lock/) || [];
if (podfileMatch.length == 0) {
linesToKeep += line + '\n';
}
}
(0, utils_fs_1.writeFileSync)(filename, linesToKeep, { encoding: 'utf-8' });
}
async function updateAppIcons(config) {
const iconToKeep = 'AppIcon-512@2x.png';
const contentsFile = 'Contents.json';
const newContentsFileContents = `{
"images" : [
{
"filename" : "${iconToKeep}",
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}`;
const path = (0, path_1.join)(config.ios.platformDirAbs, 'App', 'App', 'Assets.xcassets', 'AppIcon.appiconset');
if (!(0, utils_fs_1.existsSync)(path)) {
log_1.logger.error(`Unable to find ${path}. Try updating it manually`);
return;
}
if (!(0, utils_fs_1.existsSync)((0, path_1.join)(path, iconToKeep))) {
log_1.logger.error(`Unable to find ${iconToKeep}. Try updating it manually`);
return;
}
if (!(0, utils_fs_1.existsSync)((0, path_1.join)(path, contentsFile))) {
log_1.logger.error(`Unable to find ${path}. Try updating it manually`);
return;
}
const filenames = (0, utils_fs_1.readdirSync)(path);
for (const filename of filenames) {
if (filename != iconToKeep && filename != contentsFile) {
(0, utils_fs_1.removeSync)((0, path_1.join)(path, filename));
}
}
(0, utils_fs_1.writeFileSync)((0, path_1.join)(path, contentsFile), newContentsFileContents);
}
async function updateGradleProperties(filename) {
const txt = readFile(filename);
if (!txt) {
return;
}
const lines = txt.split('\n');
let linesToKeep = '';
for (const line of lines) {
// check for enableJetifier
const jetifierMatch = line.match(/android\.enableJetifier\s*=\s*true/) || [];
const commentMatch = line.match(/# Automatically convert third-party libraries to use AndroidX/) || [];
if (jetifierMatch.length == 0 && commentMatch.length == 0) {
linesToKeep += line + '\n';
}
}
(0, utils_fs_1.writeFileSync)(filename, linesToKeep, { encoding: 'utf-8' });
}
async function movePackageFromManifestToBuildGradle(manifestFilename, buildGradleFilename) {
const manifestText = readFile(manifestFilename);
const buildGradleText = readFile(buildGradleFilename);
if (!manifestText) {
log_1.logger.error(`Could not read ${manifestFilename}. Check its permissions and if it exists.`);
return;
}
if (!buildGradleText) {
log_1.logger.error(`Could not read ${buildGradleFilename}. Check its permissions and if it exists.`);
return;
}
const namespaceExists = new RegExp(/\s+namespace\s+/).test(buildGradleText);
if (namespaceExists) {
log_1.logger.error('Found namespace in build.gradle already, skipping migration');
return;
}
let packageName;
const manifestRegEx = new RegExp(/package="([^"]+)"/);
const manifestResults = manifestRegEx.exec(manifestText);
if (manifestResults === null) {
log_1.logger.error(`Unable to update Android Manifest. Package not found.`);
return;
}
else {
packageName = manifestResults[1];
}
let manifestReplaced = manifestText;
manifestReplaced = manifestReplaced.replace(manifestRegEx, '');
if (manifestText == manifestReplaced) {
log_1.logger.error(`Unable to update Android Manifest: no changes were detected in Android Manifest file`);
return;
}
let buildGradleReplaced = buildGradleText;
buildGradleReplaced = setAllStringIn(buildGradleText, 'android {', '\n', `\n namespace "${packageName}"`);
if (buildGradleText == buildGradleReplaced) {
log_1.logger.error(`Unable to update buildGradleText: no changes were detected in Android Manifest file`);
return;
}
(0, utils_fs_1.writeFileSync)(manifestFilename, manifestReplaced, 'utf-8');
(0, utils_fs_1.writeFileSync)(buildGradleFilename, buildGradleReplaced, 'utf-8');
}
async function updateBuildGradle(filename, variablesAndClasspaths) {
// In build.gradle add dependencies:
// classpath 'com.android.tools.build:gradle:8.0.0'
// classpath 'com.google.gms:google-services:4.3.15'
const txt = readFile(filename);
if (!txt) {
return;
}
const neededDeps = {
'com.android.tools.build:gradle': variablesAndClasspaths['com.android.tools.build:gradle'],
'com.google.gms:google-services': variablesAndClasspaths['com.google.gms:google-services'],
};
let replaced = txt;
for (const dep of Object.keys(neededDeps)) {
if (replaced.includes(`classpath '${dep}`)) {
const semver = await Promise.resolve().then(() => tslib_1.__importStar(require('semver')));
const firstIndex = replaced.indexOf(dep) + dep.length + 1;
const existingVersion = '' + replaced.substring(firstIndex, replaced.indexOf("'", firstIndex));
if (semver.gte(neededDeps[dep], existingVersion)) {
replaced = setAllStringIn(replaced, `classpath '${dep}:`, `'`, neededDeps[dep]);
log_1.logger.info(`Set ${dep} = ${neededDeps[dep]}.`);
}
}
}
(0, utils_fs_1.writeFileSync)(filename, replaced, 'utf-8');
}
async function updateFile(config, filename, textStart, textEnd, replacement, skipIfNotFound) {
if (config === null) {
return false;
}
const path = filename;
let txt = readFile(path);
if (!txt) {
return false;
}
if (txt.includes(textStart)) {
if (replacement) {
txt = setAllStringIn(txt, textStart, textEnd, replacement);
(0, utils_fs_1.writeFileSync)(path, txt, { encoding: 'utf-8' });
}
else {
// Replacing in code so we need to count the number of brackets to find the end of the function in swift
const lines = txt.split('\n');
let replaced = '';
let keep = true;
let brackets = 0;
for (const line of lines) {
if (line.includes(textStart)) {
keep = false;
}
if (!keep) {
brackets += (line.match(/{/g) || []).length;
brackets -= (line.match(/}/g) || []).length;
if (brackets == 0) {
keep = true;
}
}
else {
replaced += line + '\n';
}
}
(0, utils_fs_1.writeFileSync)(path, replaced, { encoding: 'utf-8' });
}
return true;
}
else if (!skipIfNotFound) {
log_1.logger.error(`Unable to find "${textStart}" in ${filename}. Try updating it manually`);
}
return false;
}
function setAllStringIn(data, start, end, replacement) {
let position = 0;
let result = data;
let replaced = true;
while (replaced) {
const foundIdx = result.indexOf(start, position);
if (foundIdx == -1) {
replaced = false;
}
else {
const idx = foundIdx + start.length;
position = idx + replacement.length;
result =
result.substring(0, idx) +
replacement +
result.substring(result.indexOf(end, idx));
}
}
return result;
}
async function patchOldCapacitorPlugins(config) {
const allPlugins = await (0, plugin_1.getPlugins)(config, 'android');
const androidPlugins = await (0, common_1.getAndroidPlugins)(allPlugins);
return await Promise.all(androidPlugins.map(async (p) => {
var _a, _b;
if ((_b = (_a = p.manifest) === null || _a === void 0 ? void 0 : _a.android) === null || _b === void 0 ? void 0 : _b.src) {
const buildGradlePath = (0, node_1.resolveNode)(config.app.rootDir, p.id, p.manifest.android.src, 'build.gradle');
const manifestPath = (0, node_1.resolveNode)(config.app.rootDir, p.id, p.manifest.android.src, 'src', 'main', 'AndroidManifest.xml');
if (buildGradlePath && manifestPath) {
const gradleContent = readFile(buildGradlePath);
if (!(gradleContent === null || gradleContent === void 0 ? void 0 : gradleContent.includes('namespace'))) {
if (plugins.includes(p.id)) {
log_1.logger.warn(`You are using an outdated version of ${p.id}, update the plugin to version ${pluginVersion}`);
}
else {
log_1.logger.warn(`${p.id}@${p.version} doesn't officially support Capacitor ${coreVersion} yet, doing our best moving it's package to build.gradle so it builds`);
}
movePackageFromManifestToBuildGradle(manifestPath, buildGradlePath);
}
}
}
}));
}
exports.patchOldCapacitorPlugins = patchOldCapacitorPlugins;

View file

@ -0,0 +1,11 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.newPluginCommand = void 0;
const tslib_1 = require("tslib");
const colors_1 = tslib_1.__importDefault(require("../colors"));
const errors_1 = require("../errors");
async function newPluginCommand() {
(0, errors_1.fatal)(`The plugin:generate command has been removed.\n` +
`Use ${colors_1.default.input('npm init @capacitor/plugin')}`);
}
exports.newPluginCommand = newPluginCommand;

View file

@ -0,0 +1,62 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.open = exports.openCommand = void 0;
const tslib_1 = require("tslib");
const open_1 = require("../android/open");
const colors_1 = tslib_1.__importDefault(require("../colors"));
const common_1 = require("../common");
const errors_1 = require("../errors");
const open_2 = require("../ios/open");
const log_1 = require("../log");
async function openCommand(config, selectedPlatformName) {
var _a;
if (selectedPlatformName && !(await (0, common_1.isValidPlatform)(selectedPlatformName))) {
const platformDir = (0, common_1.resolvePlatform)(config, selectedPlatformName);
if (platformDir) {
await (0, common_1.runPlatformHook)(config, selectedPlatformName, platformDir, 'capacitor:open');
}
else {
log_1.logger.error(`Platform ${colors_1.default.input(selectedPlatformName)} not found.`);
}
}
else {
const platforms = await (0, common_1.selectPlatforms)(config, selectedPlatformName);
let platformName;
if (platforms.length === 1) {
platformName = platforms[0];
}
else {
platformName = await (0, common_1.promptForPlatform)(platforms.filter(createOpenablePlatformFilter(config)), `Please choose a platform to open:`);
}
try {
await open(config, platformName);
}
catch (e) {
if (!(0, errors_1.isFatal)(e)) {
(0, errors_1.fatal)((_a = e.stack) !== null && _a !== void 0 ? _a : e);
}
throw e;
}
}
}
exports.openCommand = openCommand;
function createOpenablePlatformFilter(config) {
return platform => platform === config.ios.name || platform === config.android.name;
}
async function open(config, platformName) {
if (platformName === config.ios.name) {
await (0, common_1.runTask)('Opening the Xcode workspace...', () => {
return (0, open_2.openIOS)(config);
});
}
else if (platformName === config.android.name) {
return (0, open_1.openAndroid)(config);
}
else if (platformName === config.web.name) {
return Promise.resolve();
}
else {
throw `Platform ${platformName} is not valid.`;
}
}
exports.open = open;

View file

@ -0,0 +1,114 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.run = exports.runCommand = void 0;
const tslib_1 = require("tslib");
const utils_process_1 = require("@ionic/utils-process");
const utils_terminal_1 = require("@ionic/utils-terminal");
const run_1 = require("../android/run");
const colors_1 = tslib_1.__importDefault(require("../colors"));
const common_1 = require("../common");
const cordova_1 = require("../cordova");
const errors_1 = require("../errors");
const run_2 = require("../ios/run");
const log_1 = require("../log");
const livereload_1 = require("../util/livereload");
const native_run_1 = require("../util/native-run");
const sync_1 = require("./sync");
async function runCommand(config, selectedPlatformName, options) {
var _a, _b, _c, _d;
options.host =
(_b = (_a = options.host) !== null && _a !== void 0 ? _a : livereload_1.CapLiveReloadHelper.getIpAddress()) !== null && _b !== void 0 ? _b : 'localhost';
options.port = (_c = options.port) !== null && _c !== void 0 ? _c : '3000';
if (selectedPlatformName && !(await (0, common_1.isValidPlatform)(selectedPlatformName))) {
const platformDir = (0, common_1.resolvePlatform)(config, selectedPlatformName);
if (platformDir) {
await (0, common_1.runPlatformHook)(config, selectedPlatformName, platformDir, 'capacitor:run');
}
else {
log_1.logger.error(`Platform ${colors_1.default.input(selectedPlatformName)} not found.`);
}
}
else {
const platforms = await (0, common_1.selectPlatforms)(config, selectedPlatformName);
let platformName;
if (platforms.length === 1) {
platformName = platforms[0];
}
else {
platformName = await (0, common_1.promptForPlatform)(platforms.filter(createRunnablePlatformFilter(config)), `Please choose a platform to run:`);
}
if (options.list) {
const targets = await (0, native_run_1.getPlatformTargets)(platformName);
const outputTargets = targets.map(t => {
var _a;
return ({
name: (0, common_1.getPlatformTargetName)(t),
api: `${t.platform === 'ios' ? 'iOS' : 'API'} ${t.sdkVersion}`,
id: (_a = t.id) !== null && _a !== void 0 ? _a : '?',
});
});
// TODO: make hidden commander option (https://github.com/tj/commander.js/issues/1106)
if (process.argv.includes('--json')) {
process.stdout.write(`${JSON.stringify(outputTargets)}\n`);
}
else {
const rows = outputTargets.map(t => [t.name, t.api, t.id]);
log_1.output.write(`${(0, utils_terminal_1.columnar)(rows, {
headers: ['Name', 'API', 'Target ID'],
vsep: ' ',
})}\n`);
}
return;
}
try {
if (options.sync) {
await (0, sync_1.sync)(config, platformName, false, true);
}
const cordovaPlugins = await (0, cordova_1.getCordovaPlugins)(config, platformName);
if (options.liveReload) {
await livereload_1.CapLiveReloadHelper.editCapConfigForLiveReload(config, platformName, options);
if (platformName === config.android.name) {
await await (0, cordova_1.writeCordovaAndroidManifest)(cordovaPlugins, config, platformName, true);
}
}
await run(config, platformName, options);
if (options.liveReload) {
new Promise(resolve => process.on('SIGINT', resolve))
.then(async () => {
await livereload_1.CapLiveReloadHelper.revertCapConfigForLiveReload();
if (platformName === config.android.name) {
await (0, cordova_1.writeCordovaAndroidManifest)(cordovaPlugins, config, platformName, false);
}
})
.then(() => process.exit());
log_1.logger.info(`App running with live reload listing for: http://${options.host}:${options.port}. Press Ctrl+C to quit.`);
await (0, utils_process_1.sleepForever)();
}
}
catch (e) {
if (!(0, errors_1.isFatal)(e)) {
(0, errors_1.fatal)((_d = e.stack) !== null && _d !== void 0 ? _d : e);
}
throw e;
}
}
}
exports.runCommand = runCommand;
async function run(config, platformName, options) {
if (platformName == config.ios.name) {
await (0, run_2.runIOS)(config, options);
}
else if (platformName === config.android.name) {
await (0, run_1.runAndroid)(config, options);
}
else if (platformName === config.web.name) {
return;
}
else {
throw `Platform ${platformName} is not valid.`;
}
}
exports.run = run;
function createRunnablePlatformFilter(config) {
return platform => platform === config.ios.name || platform === config.android.name;
}

View file

@ -0,0 +1,11 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.serveCommand = void 0;
const tslib_1 = require("tslib");
const colors_1 = tslib_1.__importDefault(require("../colors"));
const errors_1 = require("../errors");
async function serveCommand() {
(0, errors_1.fatal)(`The serve command has been removed.\n` +
`Use a third-party tool for serving single page apps, such as ${colors_1.default.strong('serve')}: ${colors_1.default.strong('https://www.npmjs.com/package/serve')}`);
}
exports.serveCommand = serveCommand;

View file

@ -0,0 +1,40 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.inlineSourceMaps = void 0;
const utils_fs_1 = require("@ionic/utils-fs");
const path_1 = require("path");
const log_1 = require("../log");
function walkDirectory(dirPath) {
const files = (0, utils_fs_1.readdirSync)(dirPath);
files.forEach(file => {
const targetFile = (0, path_1.join)(dirPath, file);
if ((0, utils_fs_1.existsSync)(targetFile) && (0, utils_fs_1.lstatSync)(targetFile).isDirectory()) {
walkDirectory(targetFile);
}
else {
const mapFile = (0, path_1.join)(dirPath, `${file}.map`);
if ((0, path_1.extname)(file) === '.js' && (0, utils_fs_1.existsSync)(mapFile)) {
const bufMap = (0, utils_fs_1.readFileSync)(mapFile).toString('base64');
const bufFile = (0, utils_fs_1.readFileSync)(targetFile, 'utf8');
const result = bufFile.replace(`sourceMappingURL=${file}.map`, 'sourceMappingURL=data:application/json;charset=utf-8;base64,' +
bufMap);
(0, utils_fs_1.writeFileSync)(targetFile, result);
(0, utils_fs_1.unlinkSync)(mapFile);
}
}
});
}
async function inlineSourceMaps(config, platformName) {
let buildDir = '';
if (platformName == config.ios.name) {
buildDir = await config.ios.webDirAbs;
}
if (platformName == config.android.name) {
buildDir = await config.android.webDirAbs;
}
if (buildDir) {
log_1.logger.info('Inlining sourcemaps');
walkDirectory(buildDir);
}
}
exports.inlineSourceMaps = inlineSourceMaps;

View file

@ -0,0 +1,59 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.sync = exports.syncCommand = void 0;
const common_1 = require("../common");
const errors_1 = require("../errors");
const log_1 = require("../log");
const promise_1 = require("../util/promise");
const copy_1 = require("./copy");
const update_1 = require("./update");
/**
* Sync is a copy and an update in one.
*/
async function syncCommand(config, selectedPlatformName, deployment, inline = false) {
var _a, _b;
if (selectedPlatformName && !(await (0, common_1.isValidPlatform)(selectedPlatformName))) {
try {
await (0, copy_1.copyCommand)(config, selectedPlatformName, inline);
}
catch (e) {
log_1.logger.error((_a = e.stack) !== null && _a !== void 0 ? _a : e);
}
await (0, update_1.updateCommand)(config, selectedPlatformName, deployment);
}
else {
const then = +new Date();
const platforms = await (0, common_1.selectPlatforms)(config, selectedPlatformName);
try {
await (0, common_1.check)([
() => (0, common_1.checkPackage)(),
() => (0, common_1.checkWebDir)(config),
...(0, update_1.updateChecks)(config, platforms),
]);
await (0, promise_1.allSerial)(platforms.map(platformName => () => sync(config, platformName, deployment, inline)));
const now = +new Date();
const diff = (now - then) / 1000;
log_1.logger.info(`Sync finished in ${diff}s`);
}
catch (e) {
if (!(0, errors_1.isFatal)(e)) {
(0, errors_1.fatal)((_b = e.stack) !== null && _b !== void 0 ? _b : e);
}
throw e;
}
}
}
exports.syncCommand = syncCommand;
async function sync(config, platformName, deployment, inline = false) {
var _a;
await (0, common_1.runPlatformHook)(config, platformName, config.app.rootDir, 'capacitor:sync:before');
try {
await (0, copy_1.copy)(config, platformName, inline);
}
catch (e) {
log_1.logger.error((_a = e.stack) !== null && _a !== void 0 ? _a : e);
}
await (0, update_1.update)(config, platformName, deployment);
await (0, common_1.runPlatformHook)(config, platformName, config.app.rootDir, 'capacitor:sync:after');
}
exports.sync = sync;

View file

@ -0,0 +1,40 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.telemetryCommand = void 0;
const tslib_1 = require("tslib");
const colors_1 = tslib_1.__importDefault(require("../colors"));
const errors_1 = require("../errors");
const log_1 = require("../log");
const sysconfig_1 = require("../sysconfig");
const telemetry_1 = require("../telemetry");
async function telemetryCommand(onOrOff) {
const sysconfig = await (0, sysconfig_1.readConfig)();
const enabled = interpretEnabled(onOrOff);
if (typeof enabled === 'boolean') {
if (sysconfig.telemetry === enabled) {
log_1.logger.info(`Telemetry is already ${colors_1.default.strong(enabled ? 'on' : 'off')}`);
}
else {
await (0, sysconfig_1.writeConfig)({ ...sysconfig, telemetry: enabled });
(0, log_1.logSuccess)(`You have ${colors_1.default.strong(`opted ${enabled ? 'in' : 'out'}`)} ${enabled ? 'for' : 'of'} telemetry on this machine.`);
if (enabled) {
log_1.output.write(telemetry_1.THANK_YOU);
}
}
}
else {
log_1.logger.info(`Telemetry is ${colors_1.default.strong(sysconfig.telemetry ? 'on' : 'off')}`);
}
}
exports.telemetryCommand = telemetryCommand;
function interpretEnabled(onOrOff) {
switch (onOrOff) {
case 'on':
return true;
case 'off':
return false;
case undefined:
return undefined;
}
(0, errors_1.fatal)(`Argument must be ${colors_1.default.strong('on')} or ${colors_1.default.strong('off')} (or left unspecified)`);
}

View file

@ -0,0 +1,74 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.update = exports.updateChecks = exports.updateCommand = void 0;
const tslib_1 = require("tslib");
const update_1 = require("../android/update");
const colors_1 = tslib_1.__importDefault(require("../colors"));
const common_1 = require("../common");
const errors_1 = require("../errors");
const common_2 = require("../ios/common");
const update_2 = require("../ios/update");
const log_1 = require("../log");
const promise_1 = require("../util/promise");
async function updateCommand(config, selectedPlatformName, deployment) {
var _a;
if (selectedPlatformName && !(await (0, common_1.isValidPlatform)(selectedPlatformName))) {
const platformDir = (0, common_1.resolvePlatform)(config, selectedPlatformName);
if (platformDir) {
await (0, common_1.runPlatformHook)(config, selectedPlatformName, platformDir, 'capacitor:update');
}
else {
log_1.logger.error(`Platform ${colors_1.default.input(selectedPlatformName)} not found.`);
}
}
else {
const then = +new Date();
const platforms = await (0, common_1.selectPlatforms)(config, selectedPlatformName);
try {
await (0, common_1.check)([() => (0, common_1.checkPackage)(), ...updateChecks(config, platforms)]);
await (0, promise_1.allSerial)(platforms.map(platformName => async () => await update(config, platformName, deployment)));
const now = +new Date();
const diff = (now - then) / 1000;
log_1.logger.info(`Update finished in ${diff}s`);
}
catch (e) {
if (!(0, errors_1.isFatal)(e)) {
(0, errors_1.fatal)((_a = e.stack) !== null && _a !== void 0 ? _a : e);
}
throw e;
}
}
}
exports.updateCommand = updateCommand;
function updateChecks(config, platforms) {
const checks = [];
for (const platformName of platforms) {
if (platformName === config.ios.name) {
checks.push(() => (0, common_2.checkBundler)(config) || (0, common_2.checkCocoaPods)(config));
}
else if (platformName === config.android.name) {
continue;
}
else if (platformName === config.web.name) {
continue;
}
else {
throw `Platform ${platformName} is not valid.`;
}
}
return checks;
}
exports.updateChecks = updateChecks;
async function update(config, platformName, deployment) {
await (0, common_1.runTask)(colors_1.default.success(colors_1.default.strong(`update ${platformName}`)), async () => {
await (0, common_1.runPlatformHook)(config, platformName, config.app.rootDir, 'capacitor:update:before');
if (platformName === config.ios.name) {
await (0, update_2.updateIOS)(config, deployment);
}
else if (platformName === config.android.name) {
await (0, update_1.updateAndroid)(config);
}
await (0, common_1.runPlatformHook)(config, platformName, config.app.rootDir, 'capacitor:update:after');
});
}
exports.update = update;

View file

@ -0,0 +1,130 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.sendMetric = exports.telemetryAction = exports.THANK_YOU = void 0;
const tslib_1 = require("tslib");
const commander_1 = require("commander");
const debug_1 = tslib_1.__importDefault(require("debug"));
const colors_1 = tslib_1.__importDefault(require("./colors"));
const ipc_1 = require("./ipc");
const log_1 = require("./log");
const sysconfig_1 = require("./sysconfig");
const subprocess_1 = require("./util/subprocess");
const term_1 = require("./util/term");
const debug = (0, debug_1.default)('capacitor:telemetry');
exports.THANK_YOU = `\nThank you for helping to make Capacitor better! 💖` +
`\nInformation about the data we collect is available on our website: ${colors_1.default.strong('https://capacitorjs.com/telemetry')}\n`;
function telemetryAction(config, action) {
return async (...actionArgs) => {
const start = new Date();
// This is how commanderjs works--the command object is either the last
// element or second to last if there are additional options (via `.allowUnknownOption()`)
const lastArg = actionArgs[actionArgs.length - 1];
const cmd = lastArg instanceof commander_1.Command ? lastArg : actionArgs[actionArgs.length - 2];
const command = getFullCommandName(cmd);
let error;
try {
await action(...actionArgs);
}
catch (e) {
error = e;
}
const end = new Date();
const duration = end.getTime() - start.getTime();
const packages = Object.entries({
...config.app.package.devDependencies,
...config.app.package.dependencies,
});
// Only collect packages in the capacitor org:
// https://www.npmjs.com/org/capacitor
const capacitorPackages = packages.filter(([k]) => k.startsWith('@capacitor/'));
const versions = capacitorPackages.map(([k, v]) => [
`${k.replace(/^@capacitor\//, '').replace(/-/g, '_')}_version`,
v,
]);
const data = {
app_id: await getAppIdentifier(config),
command,
arguments: cmd.args.join(' '),
options: JSON.stringify(cmd.opts()),
duration,
error: error ? (error.message ? error.message : String(error)) : null,
node_version: process.version,
os: config.cli.os,
...Object.fromEntries(versions),
};
if ((0, term_1.isInteractive)()) {
let sysconfig = await (0, sysconfig_1.readConfig)();
if (!error && typeof sysconfig.telemetry === 'undefined') {
const confirm = await promptForTelemetry();
sysconfig = { ...sysconfig, telemetry: confirm };
await (0, sysconfig_1.writeConfig)(sysconfig);
}
await sendMetric(sysconfig, 'capacitor_cli_command', data);
}
if (error) {
throw error;
}
};
}
exports.telemetryAction = telemetryAction;
/**
* If telemetry is enabled, send a metric via IPC to a forked process for uploading.
*/
async function sendMetric(sysconfig, name, data) {
if (sysconfig.telemetry && (0, term_1.isInteractive)()) {
const message = {
name,
timestamp: new Date().toISOString(),
session_id: sysconfig.machine,
source: 'capacitor_cli',
value: data,
};
await (0, ipc_1.send)({ type: 'telemetry', data: message });
}
else {
debug('Telemetry is off (user choice, non-interactive terminal, or CI)--not sending metric');
}
}
exports.sendMetric = sendMetric;
async function promptForTelemetry() {
const { confirm } = await (0, log_1.logPrompt)(`${colors_1.default.strong('Would you like to help improve Capacitor by sharing anonymous usage data? 💖')}\n` +
`Read more about what is being collected and why here: ${colors_1.default.strong('https://capacitorjs.com/telemetry')}. You can change your mind at any time by using the ${colors_1.default.input('npx cap telemetry')} command.`, {
type: 'confirm',
name: 'confirm',
message: 'Share anonymous usage data?',
initial: true,
});
if (confirm) {
log_1.output.write(exports.THANK_YOU);
}
return confirm;
}
/**
* Get a unique anonymous identifier for this app.
*/
async function getAppIdentifier(config) {
const { createHash } = await Promise.resolve().then(() => tslib_1.__importStar(require('crypto')));
// get the first commit hash, which should be universally unique
const output = await (0, subprocess_1.getCommandOutput)('git', ['rev-list', '--max-parents=0', 'HEAD'], { cwd: config.app.rootDir });
const firstLine = output === null || output === void 0 ? void 0 : output.split('\n')[0];
if (!firstLine) {
debug('Could not obtain unique app identifier');
return null;
}
// use sha1 to create a one-way hash to anonymize
const id = createHash('sha1').update(firstLine).digest('hex');
return id;
}
/**
* Walk through the command's parent tree and construct a space-separated name.
*
* Probably overkill because we don't have nested commands, but whatever.
*/
function getFullCommandName(cmd) {
const names = [];
while (cmd.parent !== null) {
names.push(cmd.name());
cmd = cmd.parent;
}
return names.reverse().join(' ');
}

View file

@ -0,0 +1,25 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.wrapAction = exports.ENV_PATHS = void 0;
const tslib_1 = require("tslib");
const env_paths_1 = tslib_1.__importDefault(require("env-paths"));
const errors_1 = require("../errors");
const log_1 = require("../log");
exports.ENV_PATHS = (0, env_paths_1.default)('capacitor', { suffix: '' });
function wrapAction(action) {
return async (...args) => {
try {
await action(...args);
}
catch (e) {
if ((0, errors_1.isFatal)(e)) {
process.exitCode = e.exitCode;
log_1.logger.error(e.message);
}
else {
throw e;
}
}
};
}
exports.wrapAction = wrapAction;

View file

@ -0,0 +1,13 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.emoji = void 0;
// Emoji falback, right now just uses fallback on windows,
// but could expand to be more sophisticated to allow emoji
// on Hyper term on windows, for example.
const emoji = (x, fallback) => {
if (process.platform === 'win32') {
return fallback;
}
return x;
};
exports.emoji = emoji;

View file

@ -0,0 +1,13 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.tryFn = void 0;
const tryFn = async (fn, ...args) => {
try {
return await fn(...args);
}
catch {
// ignore
}
return null;
};
exports.tryFn = tryFn;

View file

@ -0,0 +1,24 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.deleteFolderRecursive = exports.convertToUnixPath = void 0;
const utils_fs_1 = require("@ionic/utils-fs");
const path_1 = require("path");
const convertToUnixPath = (path) => {
return path.replace(/\\/g, '/');
};
exports.convertToUnixPath = convertToUnixPath;
const deleteFolderRecursive = (directoryPath) => {
if ((0, utils_fs_1.existsSync)(directoryPath)) {
(0, utils_fs_1.readdirSync)(directoryPath).forEach(file => {
const curPath = (0, path_1.join)(directoryPath, file);
if ((0, utils_fs_1.lstatSync)(curPath).isDirectory()) {
(0, exports.deleteFolderRecursive)(curPath);
}
else {
(0, utils_fs_1.unlinkSync)(curPath);
}
});
(0, utils_fs_1.rmdirSync)(directoryPath);
}
};
exports.deleteFolderRecursive = deleteFolderRecursive;

View file

@ -0,0 +1,21 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.formatJSObject = void 0;
const tslib_1 = require("tslib");
const util_1 = tslib_1.__importDefault(require("util"));
function formatJSObject(o) {
try {
o = JSON.parse(JSON.stringify(o));
}
catch (e) {
throw new Error(`Cannot parse object as JSON: ${e.stack ? e.stack : e}`);
}
return util_1.default.inspect(o, {
compact: false,
breakLength: Infinity,
depth: Infinity,
maxArrayLength: Infinity,
maxStringLength: Infinity,
});
}
exports.formatJSObject = formatJSObject;

View file

@ -0,0 +1,151 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.CapLiveReloadHelper = void 0;
const utils_fs_1 = require("@ionic/utils-fs");
const os_1 = require("os");
const path_1 = require("path");
class CapLiveReload {
constructor() {
this.configJsonToRevertTo = {
json: null,
platformPath: null,
};
// nothing to do
}
getIpAddress(name, family) {
var _a;
const interfaces = (_a = (0, os_1.networkInterfaces)()) !== null && _a !== void 0 ? _a : {};
const _normalizeFamily = (family) => {
if (family === 4) {
return 'ipv4';
}
if (family === 6) {
return 'ipv6';
}
return family ? family.toLowerCase() : 'ipv4';
};
const isLoopback = (addr) => {
return (/^(::f{4}:)?127\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})/.test(addr) ||
/^fe80::1$/.test(addr) ||
/^::1$/.test(addr) ||
/^::$/.test(addr));
};
const isPrivate = (addr) => {
return (/^(::f{4}:)?10\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})$/i.test(addr) ||
/^(::f{4}:)?192\.168\.([0-9]{1,3})\.([0-9]{1,3})$/i.test(addr) ||
/^(::f{4}:)?172\.(1[6-9]|2\d|30|31)\.([0-9]{1,3})\.([0-9]{1,3})$/i.test(addr) ||
/^(::f{4}:)?127\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})$/i.test(addr) ||
/^(::f{4}:)?169\.254\.([0-9]{1,3})\.([0-9]{1,3})$/i.test(addr) ||
/^f[cd][0-9a-f]{2}:/i.test(addr) ||
/^fe80:/i.test(addr) ||
/^::1$/.test(addr) ||
/^::$/.test(addr));
};
const isPublic = (addr) => {
return !isPrivate(addr);
};
const loopback = (family) => {
//
// Default to `ipv4`
//
family = _normalizeFamily(family);
if (family !== 'ipv4' && family !== 'ipv6') {
throw new Error('family must be ipv4 or ipv6');
}
return family === 'ipv4' ? '127.0.0.1' : 'fe80::1';
};
//
// Default to `ipv4`
//
family = _normalizeFamily(family);
//
// If a specific network interface has been named,
// return the address.
//
if (name && name !== 'private' && name !== 'public') {
const res = interfaces[name].filter((details) => {
const itemFamily = _normalizeFamily(details.family);
return itemFamily === family;
});
if (res.length === 0) {
return undefined;
}
return res[0].address;
}
const all = Object.keys(interfaces)
.map(nic => {
//
// Note: name will only be `public` or `private`
// when this is called.
//
const addresses = interfaces[nic].filter((details) => {
details.family = _normalizeFamily(details.family);
if (details.family !== family || isLoopback(details.address)) {
return false;
}
if (!name) {
return true;
}
return name === 'public'
? isPrivate(details.address)
: isPublic(details.address);
});
return addresses.length ? addresses[0].address : undefined;
})
.filter(Boolean);
return !all.length ? loopback(family) : all[0];
}
// TODO remove on next major as it's unused
async editExtConfigForLiveReload(config, platformName, options, rootConfigChange = false) {
const platformAbsPath = platformName == config.ios.name
? config.ios.nativeTargetDirAbs
: platformName == config.android.name
? config.android.assetsDirAbs
: null;
if (platformAbsPath == null)
throw new Error('Platform not found.');
const capConfigPath = rootConfigChange
? config.app.extConfigFilePath
: (0, path_1.join)(platformAbsPath, 'capacitor.config.json');
const configJson = { ...config.app.extConfig };
this.configJsonToRevertTo.json = JSON.stringify(configJson, null, 2);
this.configJsonToRevertTo.platformPath = capConfigPath;
const url = `http://${options.host}:${options.port}`;
configJson.server = {
url,
};
return configJson;
}
// TODO remove rootConfigChange param on next major as it's unused
async editCapConfigForLiveReload(config, platformName, options, rootConfigChange = false) {
const platformAbsPath = platformName == config.ios.name
? config.ios.nativeTargetDirAbs
: platformName == config.android.name
? config.android.assetsDirAbs
: null;
if (platformAbsPath == null)
throw new Error('Platform not found.');
const capConfigPath = rootConfigChange
? config.app.extConfigFilePath
: (0, path_1.join)(platformAbsPath, 'capacitor.config.json');
const configJson = (0, utils_fs_1.readJSONSync)(capConfigPath);
this.configJsonToRevertTo.json = JSON.stringify(configJson, null, 2);
this.configJsonToRevertTo.platformPath = capConfigPath;
const url = `http://${options.host}:${options.port}`;
configJson.server = {
url,
};
(0, utils_fs_1.writeJSONSync)(capConfigPath, configJson, { spaces: '\t' });
}
async revertCapConfigForLiveReload() {
if (this.configJsonToRevertTo.json == null ||
this.configJsonToRevertTo.platformPath == null)
return;
const capConfigPath = this.configJsonToRevertTo.platformPath;
const configJson = this.configJsonToRevertTo.json;
(0, utils_fs_1.writeJSONSync)(capConfigPath, JSON.parse(configJson), { spaces: '\t' });
this.configJsonToRevertTo.json = null;
this.configJsonToRevertTo.platformPath = null;
}
}
exports.CapLiveReloadHelper = new CapLiveReload();

View file

@ -0,0 +1,110 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.isNXMonorepo = exports.isMonorepo = exports.findPackageRelativePathInMonorepo = exports.findPackagePath = exports.findNXMonorepoRoot = exports.findMonorepoRoot = void 0;
const node_fs_1 = require("node:fs");
const node_path_1 = require("node:path");
/**
* Finds the monorepo root from the given path.
* @param currentPath - The current path to start searching from.
* @returns The path to the monorepo root.
* @throws An error if the monorepo root is not found.
*/
function findMonorepoRoot(currentPath) {
const packageJsonPath = (0, node_path_1.join)(currentPath, 'package.json');
const pnpmWorkspacePath = (0, node_path_1.join)(currentPath, 'pnpm-workspace.yaml');
if ((0, node_fs_1.existsSync)(pnpmWorkspacePath) ||
((0, node_fs_1.existsSync)(packageJsonPath) &&
JSON.parse((0, node_fs_1.readFileSync)(packageJsonPath, 'utf-8')).workspaces)) {
return currentPath;
}
const parentPath = (0, node_path_1.dirname)(currentPath);
if (parentPath === currentPath) {
throw new Error('Monorepo root not found');
}
return findMonorepoRoot(parentPath);
}
exports.findMonorepoRoot = findMonorepoRoot;
/**
* Finds the NX monorepo root from the given path.
* @param currentPath - The current path to start searching from.
* @returns The path to the monorepo root.
* @throws An error if the monorepo root is not found.
*/
function findNXMonorepoRoot(currentPath) {
const nxJsonPath = (0, node_path_1.join)(currentPath, 'nx.json');
if ((0, node_fs_1.existsSync)(nxJsonPath)) {
return currentPath;
}
const parentPath = (0, node_path_1.dirname)(currentPath);
if (parentPath === currentPath) {
throw new Error('Monorepo root not found');
}
return findNXMonorepoRoot(parentPath);
}
exports.findNXMonorepoRoot = findNXMonorepoRoot;
/**
* Finds the path to a package within the node_modules folder,
* searching up the directory hierarchy until the last possible directory is reached.
* @param packageName - The name of the package to find.
* @param currentPath - The current path to start searching from.
* @param lastPossibleDirectory - The last possible directory to search for the package.
* @returns The path to the package, or null if not found.
*/
function findPackagePath(packageName, currentPath, lastPossibleDirectory) {
const nodeModulesPath = (0, node_path_1.join)(currentPath, 'node_modules', packageName);
if ((0, node_fs_1.existsSync)(nodeModulesPath)) {
return nodeModulesPath;
}
if (currentPath === lastPossibleDirectory) {
return null;
}
const parentPath = (0, node_path_1.dirname)(currentPath);
return findPackagePath(packageName, parentPath, lastPossibleDirectory);
}
exports.findPackagePath = findPackagePath;
/**
* Finds the relative path to a package from the current directory,
* using the monorepo root as the last possible directory.
* @param packageName - The name of the package to find.
* @param currentPath - The current path to start searching from.
* @returns The relative path to the package, or null if not found.
*/
function findPackageRelativePathInMonorepo(packageName, currentPath) {
const monorepoRoot = findMonorepoRoot(currentPath);
const packagePath = findPackagePath(packageName, currentPath, monorepoRoot);
if (packagePath) {
return (0, node_path_1.relative)(currentPath, packagePath);
}
return null;
}
exports.findPackageRelativePathInMonorepo = findPackageRelativePathInMonorepo;
/**
* Detects if the current directory is part of a monorepo (npm, yarn, pnpm).
* @param currentPath - The current path to start searching from.
* @returns True if the current directory is part of a monorepo, false otherwise.
*/
function isMonorepo(currentPath) {
try {
findMonorepoRoot(currentPath);
return true;
}
catch (error) {
return false;
}
}
exports.isMonorepo = isMonorepo;
/**
* Detects if the current directory is part of a nx integrated monorepo.
* @param currentPath - The current path to start searching from.
* @returns True if the current directory is part of a monorepo, false otherwise.
*/
function isNXMonorepo(currentPath) {
try {
findNXMonorepoRoot(currentPath);
return true;
}
catch (error) {
return false;
}
}
exports.isNXMonorepo = isNXMonorepo;

View file

@ -0,0 +1,53 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getPlatformTargets = exports.runNativeRun = void 0;
const tslib_1 = require("tslib");
const path_1 = require("path");
const colors_1 = tslib_1.__importDefault(require("../colors"));
const errors_1 = require("../errors");
const node_1 = require("./node");
const subprocess_1 = require("./subprocess");
async function runNativeRun(args, options = {}) {
const p = (0, node_1.resolveNode)(__dirname, (0, path_1.dirname)('native-run/package'), 'bin/native-run');
if (!p) {
(0, errors_1.fatal)(`${colors_1.default.input('native-run')} not found.`);
}
return await (0, subprocess_1.runCommand)(p, args, options);
}
exports.runNativeRun = runNativeRun;
async function getPlatformTargets(platformName) {
const errors = [];
try {
const output = await runNativeRun([platformName, '--list', '--json']);
const parsedOutput = JSON.parse(output);
if (parsedOutput.devices.length || parsedOutput.virtualDevices.length) {
return [
...parsedOutput.devices.map((t) => ({ ...t, virtual: false })),
...parsedOutput.virtualDevices.map((t) => ({
...t,
virtual: true,
})),
];
}
else {
parsedOutput.errors.map((e) => {
errors.push(e);
});
}
}
catch (e) {
const err = JSON.parse(e);
errors.push(err);
}
const plural = errors.length > 1 ? 's' : '';
const errMsg = `${colors_1.default.strong('native-run')} failed with error${plural}\n
${errors
.map((e) => {
return `\t${colors_1.default.strong(e.code)}: ${e.error}`;
})
.join('\n')}
\n\tMore details for this error${plural} may be available online: ${colors_1.default.strong('https://github.com/ionic-team/native-run/wiki/Android-Errors')}
`;
throw errMsg;
}
exports.getPlatformTargets = getPlatformTargets;

View file

@ -0,0 +1,54 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.resolveNode = exports.requireTS = void 0;
const utils_fs_1 = require("@ionic/utils-fs");
const fs_1 = require("fs");
const path_1 = require("path");
/**
* @see https://github.com/ionic-team/stencil/blob/HEAD/src/compiler/sys/node-require.ts
*/
const requireTS = (ts, p) => {
const id = (0, path_1.resolve)(p);
delete require.cache[id];
require.extensions['.ts'] = (module, fileName) => {
var _a;
let sourceText = (0, utils_fs_1.readFileSync)(fileName, 'utf8');
if (fileName.endsWith('.ts')) {
const tsResults = ts.transpileModule(sourceText, {
fileName,
compilerOptions: {
module: ts.ModuleKind.CommonJS,
moduleResolution: ts.ModuleResolutionKind.NodeJs,
esModuleInterop: true,
strict: true,
target: ts.ScriptTarget.ES2017,
},
reportDiagnostics: true,
});
sourceText = tsResults.outputText;
}
else {
// quick hack to turn a modern es module
// into and old school commonjs module
sourceText = sourceText.replace(/export\s+\w+\s+(\w+)/gm, 'exports.$1');
}
(_a = module._compile) === null || _a === void 0 ? void 0 : _a.call(module, sourceText, fileName);
};
const m = require(id); // eslint-disable-line @typescript-eslint/no-var-requires
delete require.extensions['.ts'];
return m;
};
exports.requireTS = requireTS;
function resolveNode(root, ...pathSegments) {
try {
return require.resolve(pathSegments.join('/'), { paths: [root] });
}
catch (e) {
const path = [root, 'node_modules', ...pathSegments].join('/');
if ((0, fs_1.existsSync)(path)) {
return path;
}
return null;
}
}
exports.resolveNode = resolveNode;

View file

@ -0,0 +1,35 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.lazy = exports.LazyPromise = exports.allSerial = void 0;
function allSerial(funcs) {
return funcs.reduce((promise, func) => promise.then(result => func().then(x => result.concat(x))), Promise.resolve([]));
}
exports.allSerial = allSerial;
class LazyPromise extends Promise {
constructor(executor) {
super(() => {
/* ignore */
});
this._executor = executor;
}
then(onfulfilled, onrejected) {
this._promise = this._promise || new Promise(this._executor);
return this._promise.then(onfulfilled, onrejected);
}
catch(onrejected) {
this._promise = this._promise || new Promise(this._executor);
return this._promise.catch(onrejected);
}
}
exports.LazyPromise = LazyPromise;
function lazy(fn) {
return new LazyPromise(async (resolve, reject) => {
try {
resolve(await fn());
}
catch (e) {
reject(e);
}
});
}
exports.lazy = lazy;

View file

@ -0,0 +1,43 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.isInstalled = exports.getCommandOutput = exports.runCommand = void 0;
const utils_subprocess_1 = require("@ionic/utils-subprocess");
async function runCommand(command, args, options = {}) {
const p = new utils_subprocess_1.Subprocess(command, args, options);
try {
return await p.output();
}
catch (e) {
if (e instanceof utils_subprocess_1.SubprocessError) {
// old behavior of just throwing the stdout/stderr strings
throw e.output
? e.output
: e.code
? e.code
: e.error
? e.error.message
: 'Unknown error';
}
throw e;
}
}
exports.runCommand = runCommand;
async function getCommandOutput(command, args, options = {}) {
try {
return (await runCommand(command, args, options)).trim();
}
catch (e) {
return null;
}
}
exports.getCommandOutput = getCommandOutput;
async function isInstalled(command) {
try {
await (0, utils_subprocess_1.which)(command);
}
catch (e) {
return false;
}
return true;
}
exports.isInstalled = isInstalled;

View file

@ -0,0 +1,11 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.extractTemplate = void 0;
const tslib_1 = require("tslib");
const utils_fs_1 = require("@ionic/utils-fs");
const tar_1 = tslib_1.__importDefault(require("tar"));
async function extractTemplate(src, dir) {
await (0, utils_fs_1.mkdirp)(dir);
await tar_1.default.extract({ file: src, cwd: dir });
}
exports.extractTemplate = extractTemplate;

View file

@ -0,0 +1,30 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.isInteractive = exports.checkInteractive = void 0;
const tslib_1 = require("tslib");
const utils_terminal_1 = require("@ionic/utils-terminal");
const colors_1 = tslib_1.__importDefault(require("../colors"));
const log_1 = require("../log");
// Given input variables to a command, make sure all are provided if the terminal
// is not interactive (because we won't be able to prompt the user)
const checkInteractive = (...args) => {
if ((0, exports.isInteractive)()) {
return true;
}
// Fail if no args are provided, treat this as just a check of whether the term is
// interactive or not.
if (!args.length) {
return false;
}
// Make sure none of the provided args are empty, otherwise print the interactive
// warning and return false
if (args.filter(arg => !arg).length) {
log_1.logger.error(`Non-interactive shell detected.\n` +
`Run the command with ${colors_1.default.input('--help')} to see a list of arguments that must be provided.`);
return false;
}
return true;
};
exports.checkInteractive = checkInteractive;
const isInteractive = () => utils_terminal_1.TERMINAL_INFO.tty && !utils_terminal_1.TERMINAL_INFO.ci;
exports.isInteractive = isInteractive;

View file

@ -0,0 +1,11 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.uuidv4 = void 0;
function uuidv4() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {
const r = (Math.random() * 16) | 0;
const v = c == 'x' ? r : (r & 0x3) | 0x8;
return v.toString(16);
});
}
exports.uuidv4 = uuidv4;

View file

@ -0,0 +1,57 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.buildXmlElement = exports.writeXML = exports.parseXML = exports.readXML = void 0;
const tslib_1 = require("tslib");
const utils_fs_1 = require("@ionic/utils-fs");
const xml2js_1 = tslib_1.__importDefault(require("xml2js"));
async function readXML(path) {
var _a;
try {
const xmlStr = await (0, utils_fs_1.readFile)(path, { encoding: 'utf-8' });
try {
return await xml2js_1.default.parseStringPromise(xmlStr);
}
catch (e) {
throw `Error parsing: ${path}, ${(_a = e.stack) !== null && _a !== void 0 ? _a : e}`;
}
}
catch (e) {
throw `Unable to read: ${path}`;
}
}
exports.readXML = readXML;
function parseXML(xmlStr, options) {
const parser = options !== undefined
? new xml2js_1.default.Parser({ ...options })
: new xml2js_1.default.Parser();
let xmlObj;
parser.parseString(xmlStr, (err, result) => {
if (!err) {
xmlObj = result;
}
});
return xmlObj;
}
exports.parseXML = parseXML;
async function writeXML(object) {
return new Promise(resolve => {
const builder = new xml2js_1.default.Builder({
headless: true,
explicitRoot: false,
rootName: 'deleteme',
});
let xml = builder.buildObject(object);
xml = xml.replace('<deleteme>', '').replace('</deleteme>', '');
resolve(xml);
});
}
exports.writeXML = writeXML;
function buildXmlElement(configElement, rootName) {
const builder = new xml2js_1.default.Builder({
headless: true,
explicitRoot: false,
rootName: rootName,
});
return builder.buildObject(configElement);
}
exports.buildXmlElement = buildXmlElement;

View file

@ -0,0 +1,22 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.copyWeb = void 0;
const tslib_1 = require("tslib");
const utils_fs_1 = require("@ionic/utils-fs");
const path_1 = require("path");
const colors_1 = tslib_1.__importDefault(require("../colors"));
const common_1 = require("../common");
const errors_1 = require("../errors");
const node_1 = require("../util/node");
async function copyWeb(config) {
if (config.app.bundledWebRuntime) {
const runtimePath = (0, node_1.resolveNode)(config.app.rootDir, '@capacitor/core', 'dist', 'capacitor.js');
if (!runtimePath) {
(0, errors_1.fatal)(`Unable to find ${colors_1.default.strong('node_modules/@capacitor/core/dist/capacitor.js')}.\n` + `Are you sure ${colors_1.default.strong('@capacitor/core')} is installed?`);
}
return (0, common_1.runTask)(`Copying ${colors_1.default.strong('capacitor.js')} to web dir`, () => {
return (0, utils_fs_1.copy)(runtimePath, (0, path_1.join)(config.app.webDirAbs, 'capacitor.js'));
});
}
}
exports.copyWeb = copyWeb;

View file

@ -0,0 +1,22 @@
(The MIT License)
Copyright (c) 2011 TJ Holowaychuk <tj@vision-media.ca>
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
'Software'), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,16 @@
import commander from './index.js';
// wrapper to provide named exports for ESM.
export const {
program,
createCommand,
createArgument,
createOption,
CommanderError,
InvalidArgumentError,
InvalidOptionArgumentError, // deprecated old name
Command,
Argument,
Option,
Help
} = commander;

View file

@ -0,0 +1,27 @@
const { Argument } = require('./lib/argument.js');
const { Command } = require('./lib/command.js');
const { CommanderError, InvalidArgumentError } = require('./lib/error.js');
const { Help } = require('./lib/help.js');
const { Option } = require('./lib/option.js');
// @ts-check
/**
* Expose the root command.
*/
exports = module.exports = new Command();
exports.program = exports; // More explicit access to global command.
// Implicit export of createArgument, createCommand, and createOption.
/**
* Expose classes
*/
exports.Argument = Argument;
exports.Command = Command;
exports.CommanderError = CommanderError;
exports.Help = Help;
exports.InvalidArgumentError = InvalidArgumentError;
exports.InvalidOptionArgumentError = InvalidArgumentError; // Deprecated
exports.Option = Option;

View file

@ -0,0 +1,147 @@
const { InvalidArgumentError } = require('./error.js');
// @ts-check
class Argument {
/**
* Initialize a new command argument with the given name and description.
* The default is that the argument is required, and you can explicitly
* indicate this with <> around the name. Put [] around the name for an optional argument.
*
* @param {string} name
* @param {string} [description]
*/
constructor(name, description) {
this.description = description || '';
this.variadic = false;
this.parseArg = undefined;
this.defaultValue = undefined;
this.defaultValueDescription = undefined;
this.argChoices = undefined;
switch (name[0]) {
case '<': // e.g. <required>
this.required = true;
this._name = name.slice(1, -1);
break;
case '[': // e.g. [optional]
this.required = false;
this._name = name.slice(1, -1);
break;
default:
this.required = true;
this._name = name;
break;
}
if (this._name.length > 3 && this._name.slice(-3) === '...') {
this.variadic = true;
this._name = this._name.slice(0, -3);
}
}
/**
* Return argument name.
*
* @return {string}
*/
name() {
return this._name;
}
/**
* @api private
*/
_concatValue(value, previous) {
if (previous === this.defaultValue || !Array.isArray(previous)) {
return [value];
}
return previous.concat(value);
}
/**
* Set the default value, and optionally supply the description to be displayed in the help.
*
* @param {any} value
* @param {string} [description]
* @return {Argument}
*/
default(value, description) {
this.defaultValue = value;
this.defaultValueDescription = description;
return this;
}
/**
* Set the custom handler for processing CLI command arguments into argument values.
*
* @param {Function} [fn]
* @return {Argument}
*/
argParser(fn) {
this.parseArg = fn;
return this;
}
/**
* Only allow argument value to be one of choices.
*
* @param {string[]} values
* @return {Argument}
*/
choices(values) {
this.argChoices = values.slice();
this.parseArg = (arg, previous) => {
if (!this.argChoices.includes(arg)) {
throw new InvalidArgumentError(`Allowed choices are ${this.argChoices.join(', ')}.`);
}
if (this.variadic) {
return this._concatValue(arg, previous);
}
return arg;
};
return this;
}
/**
* Make argument required.
*/
argRequired() {
this.required = true;
return this;
}
/**
* Make argument optional.
*/
argOptional() {
this.required = false;
return this;
}
}
/**
* Takes an argument and returns its human readable equivalent for help usage.
*
* @param {Argument} arg
* @return {string}
* @api private
*/
function humanReadableArgName(arg) {
const nameOutput = arg.name() + (arg.variadic === true ? '...' : '');
return arg.required
? '<' + nameOutput + '>'
: '[' + nameOutput + ']';
}
exports.Argument = Argument;
exports.humanReadableArgName = humanReadableArgName;

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,45 @@
// @ts-check
/**
* CommanderError class
* @class
*/
class CommanderError extends Error {
/**
* Constructs the CommanderError class
* @param {number} exitCode suggested exit code which could be used with process.exit
* @param {string} code an id string representing the error
* @param {string} message human-readable description of the error
* @constructor
*/
constructor(exitCode, code, message) {
super(message);
// properly capture stack trace in Node.js
Error.captureStackTrace(this, this.constructor);
this.name = this.constructor.name;
this.code = code;
this.exitCode = exitCode;
this.nestedError = undefined;
}
}
/**
* InvalidArgumentError class
* @class
*/
class InvalidArgumentError extends CommanderError {
/**
* Constructs the InvalidArgumentError class
* @param {string} [message] explanation of why argument is invalid
* @constructor
*/
constructor(message) {
super(1, 'commander.invalidArgument', message);
// properly capture stack trace in Node.js
Error.captureStackTrace(this, this.constructor);
this.name = this.constructor.name;
}
}
exports.CommanderError = CommanderError;
exports.InvalidArgumentError = InvalidArgumentError;

View file

@ -0,0 +1,461 @@
const { humanReadableArgName } = require('./argument.js');
/**
* TypeScript import types for JSDoc, used by Visual Studio Code IntelliSense and `npm run typescript-checkJS`
* https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html#import-types
* @typedef { import("./argument.js").Argument } Argument
* @typedef { import("./command.js").Command } Command
* @typedef { import("./option.js").Option } Option
*/
// @ts-check
// Although this is a class, methods are static in style to allow override using subclass or just functions.
class Help {
constructor() {
this.helpWidth = undefined;
this.sortSubcommands = false;
this.sortOptions = false;
this.showGlobalOptions = false;
}
/**
* Get an array of the visible subcommands. Includes a placeholder for the implicit help command, if there is one.
*
* @param {Command} cmd
* @returns {Command[]}
*/
visibleCommands(cmd) {
const visibleCommands = cmd.commands.filter(cmd => !cmd._hidden);
if (cmd._hasImplicitHelpCommand()) {
// Create a command matching the implicit help command.
const [, helpName, helpArgs] = cmd._helpCommandnameAndArgs.match(/([^ ]+) *(.*)/);
const helpCommand = cmd.createCommand(helpName)
.helpOption(false);
helpCommand.description(cmd._helpCommandDescription);
if (helpArgs) helpCommand.arguments(helpArgs);
visibleCommands.push(helpCommand);
}
if (this.sortSubcommands) {
visibleCommands.sort((a, b) => {
// @ts-ignore: overloaded return type
return a.name().localeCompare(b.name());
});
}
return visibleCommands;
}
/**
* Compare options for sort.
*
* @param {Option} a
* @param {Option} b
* @returns number
*/
compareOptions(a, b) {
const getSortKey = (option) => {
// WYSIWYG for order displayed in help. Short used for comparison if present. No special handling for negated.
return option.short ? option.short.replace(/^-/, '') : option.long.replace(/^--/, '');
};
return getSortKey(a).localeCompare(getSortKey(b));
}
/**
* Get an array of the visible options. Includes a placeholder for the implicit help option, if there is one.
*
* @param {Command} cmd
* @returns {Option[]}
*/
visibleOptions(cmd) {
const visibleOptions = cmd.options.filter((option) => !option.hidden);
// Implicit help
const showShortHelpFlag = cmd._hasHelpOption && cmd._helpShortFlag && !cmd._findOption(cmd._helpShortFlag);
const showLongHelpFlag = cmd._hasHelpOption && !cmd._findOption(cmd._helpLongFlag);
if (showShortHelpFlag || showLongHelpFlag) {
let helpOption;
if (!showShortHelpFlag) {
helpOption = cmd.createOption(cmd._helpLongFlag, cmd._helpDescription);
} else if (!showLongHelpFlag) {
helpOption = cmd.createOption(cmd._helpShortFlag, cmd._helpDescription);
} else {
helpOption = cmd.createOption(cmd._helpFlags, cmd._helpDescription);
}
visibleOptions.push(helpOption);
}
if (this.sortOptions) {
visibleOptions.sort(this.compareOptions);
}
return visibleOptions;
}
/**
* Get an array of the visible global options. (Not including help.)
*
* @param {Command} cmd
* @returns {Option[]}
*/
visibleGlobalOptions(cmd) {
if (!this.showGlobalOptions) return [];
const globalOptions = [];
for (let parentCmd = cmd.parent; parentCmd; parentCmd = parentCmd.parent) {
const visibleOptions = parentCmd.options.filter((option) => !option.hidden);
globalOptions.push(...visibleOptions);
}
if (this.sortOptions) {
globalOptions.sort(this.compareOptions);
}
return globalOptions;
}
/**
* Get an array of the arguments if any have a description.
*
* @param {Command} cmd
* @returns {Argument[]}
*/
visibleArguments(cmd) {
// Side effect! Apply the legacy descriptions before the arguments are displayed.
if (cmd._argsDescription) {
cmd._args.forEach(argument => {
argument.description = argument.description || cmd._argsDescription[argument.name()] || '';
});
}
// If there are any arguments with a description then return all the arguments.
if (cmd._args.find(argument => argument.description)) {
return cmd._args;
}
return [];
}
/**
* Get the command term to show in the list of subcommands.
*
* @param {Command} cmd
* @returns {string}
*/
subcommandTerm(cmd) {
// Legacy. Ignores custom usage string, and nested commands.
const args = cmd._args.map(arg => humanReadableArgName(arg)).join(' ');
return cmd._name +
(cmd._aliases[0] ? '|' + cmd._aliases[0] : '') +
(cmd.options.length ? ' [options]' : '') + // simplistic check for non-help option
(args ? ' ' + args : '');
}
/**
* Get the option term to show in the list of options.
*
* @param {Option} option
* @returns {string}
*/
optionTerm(option) {
return option.flags;
}
/**
* Get the argument term to show in the list of arguments.
*
* @param {Argument} argument
* @returns {string}
*/
argumentTerm(argument) {
return argument.name();
}
/**
* Get the longest command term length.
*
* @param {Command} cmd
* @param {Help} helper
* @returns {number}
*/
longestSubcommandTermLength(cmd, helper) {
return helper.visibleCommands(cmd).reduce((max, command) => {
return Math.max(max, helper.subcommandTerm(command).length);
}, 0);
}
/**
* Get the longest option term length.
*
* @param {Command} cmd
* @param {Help} helper
* @returns {number}
*/
longestOptionTermLength(cmd, helper) {
return helper.visibleOptions(cmd).reduce((max, option) => {
return Math.max(max, helper.optionTerm(option).length);
}, 0);
}
/**
* Get the longest global option term length.
*
* @param {Command} cmd
* @param {Help} helper
* @returns {number}
*/
longestGlobalOptionTermLength(cmd, helper) {
return helper.visibleGlobalOptions(cmd).reduce((max, option) => {
return Math.max(max, helper.optionTerm(option).length);
}, 0);
}
/**
* Get the longest argument term length.
*
* @param {Command} cmd
* @param {Help} helper
* @returns {number}
*/
longestArgumentTermLength(cmd, helper) {
return helper.visibleArguments(cmd).reduce((max, argument) => {
return Math.max(max, helper.argumentTerm(argument).length);
}, 0);
}
/**
* Get the command usage to be displayed at the top of the built-in help.
*
* @param {Command} cmd
* @returns {string}
*/
commandUsage(cmd) {
// Usage
let cmdName = cmd._name;
if (cmd._aliases[0]) {
cmdName = cmdName + '|' + cmd._aliases[0];
}
let parentCmdNames = '';
for (let parentCmd = cmd.parent; parentCmd; parentCmd = parentCmd.parent) {
parentCmdNames = parentCmd.name() + ' ' + parentCmdNames;
}
return parentCmdNames + cmdName + ' ' + cmd.usage();
}
/**
* Get the description for the command.
*
* @param {Command} cmd
* @returns {string}
*/
commandDescription(cmd) {
// @ts-ignore: overloaded return type
return cmd.description();
}
/**
* Get the subcommand summary to show in the list of subcommands.
* (Fallback to description for backwards compatibility.)
*
* @param {Command} cmd
* @returns {string}
*/
subcommandDescription(cmd) {
// @ts-ignore: overloaded return type
return cmd.summary() || cmd.description();
}
/**
* Get the option description to show in the list of options.
*
* @param {Option} option
* @return {string}
*/
optionDescription(option) {
const extraInfo = [];
if (option.argChoices) {
extraInfo.push(
// use stringify to match the display of the default value
`choices: ${option.argChoices.map((choice) => JSON.stringify(choice)).join(', ')}`);
}
if (option.defaultValue !== undefined) {
// default for boolean and negated more for programmer than end user,
// but show true/false for boolean option as may be for hand-rolled env or config processing.
const showDefault = option.required || option.optional ||
(option.isBoolean() && typeof option.defaultValue === 'boolean');
if (showDefault) {
extraInfo.push(`default: ${option.defaultValueDescription || JSON.stringify(option.defaultValue)}`);
}
}
// preset for boolean and negated are more for programmer than end user
if (option.presetArg !== undefined && option.optional) {
extraInfo.push(`preset: ${JSON.stringify(option.presetArg)}`);
}
if (option.envVar !== undefined) {
extraInfo.push(`env: ${option.envVar}`);
}
if (extraInfo.length > 0) {
return `${option.description} (${extraInfo.join(', ')})`;
}
return option.description;
}
/**
* Get the argument description to show in the list of arguments.
*
* @param {Argument} argument
* @return {string}
*/
argumentDescription(argument) {
const extraInfo = [];
if (argument.argChoices) {
extraInfo.push(
// use stringify to match the display of the default value
`choices: ${argument.argChoices.map((choice) => JSON.stringify(choice)).join(', ')}`);
}
if (argument.defaultValue !== undefined) {
extraInfo.push(`default: ${argument.defaultValueDescription || JSON.stringify(argument.defaultValue)}`);
}
if (extraInfo.length > 0) {
const extraDescripton = `(${extraInfo.join(', ')})`;
if (argument.description) {
return `${argument.description} ${extraDescripton}`;
}
return extraDescripton;
}
return argument.description;
}
/**
* Generate the built-in help text.
*
* @param {Command} cmd
* @param {Help} helper
* @returns {string}
*/
formatHelp(cmd, helper) {
const termWidth = helper.padWidth(cmd, helper);
const helpWidth = helper.helpWidth || 80;
const itemIndentWidth = 2;
const itemSeparatorWidth = 2; // between term and description
function formatItem(term, description) {
if (description) {
const fullText = `${term.padEnd(termWidth + itemSeparatorWidth)}${description}`;
return helper.wrap(fullText, helpWidth - itemIndentWidth, termWidth + itemSeparatorWidth);
}
return term;
}
function formatList(textArray) {
return textArray.join('\n').replace(/^/gm, ' '.repeat(itemIndentWidth));
}
// Usage
let output = [`Usage: ${helper.commandUsage(cmd)}`, ''];
// Description
const commandDescription = helper.commandDescription(cmd);
if (commandDescription.length > 0) {
output = output.concat([commandDescription, '']);
}
// Arguments
const argumentList = helper.visibleArguments(cmd).map((argument) => {
return formatItem(helper.argumentTerm(argument), helper.argumentDescription(argument));
});
if (argumentList.length > 0) {
output = output.concat(['Arguments:', formatList(argumentList), '']);
}
// Options
const optionList = helper.visibleOptions(cmd).map((option) => {
return formatItem(helper.optionTerm(option), helper.optionDescription(option));
});
if (optionList.length > 0) {
output = output.concat(['Options:', formatList(optionList), '']);
}
if (this.showGlobalOptions) {
const globalOptionList = helper.visibleGlobalOptions(cmd).map((option) => {
return formatItem(helper.optionTerm(option), helper.optionDescription(option));
});
if (globalOptionList.length > 0) {
output = output.concat(['Global Options:', formatList(globalOptionList), '']);
}
}
// Commands
const commandList = helper.visibleCommands(cmd).map((cmd) => {
return formatItem(helper.subcommandTerm(cmd), helper.subcommandDescription(cmd));
});
if (commandList.length > 0) {
output = output.concat(['Commands:', formatList(commandList), '']);
}
return output.join('\n');
}
/**
* Calculate the pad width from the maximum term length.
*
* @param {Command} cmd
* @param {Help} helper
* @returns {number}
*/
padWidth(cmd, helper) {
return Math.max(
helper.longestOptionTermLength(cmd, helper),
helper.longestGlobalOptionTermLength(cmd, helper),
helper.longestSubcommandTermLength(cmd, helper),
helper.longestArgumentTermLength(cmd, helper)
);
}
/**
* Wrap the given string to width characters per line, with lines after the first indented.
* Do not wrap if insufficient room for wrapping (minColumnWidth), or string is manually formatted.
*
* @param {string} str
* @param {number} width
* @param {number} indent
* @param {number} [minColumnWidth=40]
* @return {string}
*
*/
wrap(str, width, indent, minColumnWidth = 40) {
// Detect manually wrapped and indented strings by searching for line breaks
// followed by multiple spaces/tabs.
if (str.match(/[\n]\s+/)) return str;
// Do not wrap if not enough room for a wrapped column of text (as could end up with a word per line).
const columnWidth = width - indent;
if (columnWidth < minColumnWidth) return str;
const leadingStr = str.slice(0, indent);
const columnText = str.slice(indent);
const indentString = ' '.repeat(indent);
const regex = new RegExp('.{1,' + (columnWidth - 1) + '}([\\s\u200B]|$)|[^\\s\u200B]+?([\\s\u200B]|$)', 'g');
const lines = columnText.match(regex) || [];
return leadingStr + lines.map((line, i) => {
if (line.slice(-1) === '\n') {
line = line.slice(0, line.length - 1);
}
return ((i > 0) ? indentString : '') + line.trimRight();
}).join('\n');
}
}
exports.Help = Help;

View file

@ -0,0 +1,326 @@
const { InvalidArgumentError } = require('./error.js');
// @ts-check
class Option {
/**
* Initialize a new `Option` with the given `flags` and `description`.
*
* @param {string} flags
* @param {string} [description]
*/
constructor(flags, description) {
this.flags = flags;
this.description = description || '';
this.required = flags.includes('<'); // A value must be supplied when the option is specified.
this.optional = flags.includes('['); // A value is optional when the option is specified.
// variadic test ignores <value,...> et al which might be used to describe custom splitting of single argument
this.variadic = /\w\.\.\.[>\]]$/.test(flags); // The option can take multiple values.
this.mandatory = false; // The option must have a value after parsing, which usually means it must be specified on command line.
const optionFlags = splitOptionFlags(flags);
this.short = optionFlags.shortFlag;
this.long = optionFlags.longFlag;
this.negate = false;
if (this.long) {
this.negate = this.long.startsWith('--no-');
}
this.defaultValue = undefined;
this.defaultValueDescription = undefined;
this.presetArg = undefined;
this.envVar = undefined;
this.parseArg = undefined;
this.hidden = false;
this.argChoices = undefined;
this.conflictsWith = [];
this.implied = undefined;
}
/**
* Set the default value, and optionally supply the description to be displayed in the help.
*
* @param {any} value
* @param {string} [description]
* @return {Option}
*/
default(value, description) {
this.defaultValue = value;
this.defaultValueDescription = description;
return this;
}
/**
* Preset to use when option used without option-argument, especially optional but also boolean and negated.
* The custom processing (parseArg) is called.
*
* @example
* new Option('--color').default('GREYSCALE').preset('RGB');
* new Option('--donate [amount]').preset('20').argParser(parseFloat);
*
* @param {any} arg
* @return {Option}
*/
preset(arg) {
this.presetArg = arg;
return this;
}
/**
* Add option name(s) that conflict with this option.
* An error will be displayed if conflicting options are found during parsing.
*
* @example
* new Option('--rgb').conflicts('cmyk');
* new Option('--js').conflicts(['ts', 'jsx']);
*
* @param {string | string[]} names
* @return {Option}
*/
conflicts(names) {
this.conflictsWith = this.conflictsWith.concat(names);
return this;
}
/**
* Specify implied option values for when this option is set and the implied options are not.
*
* The custom processing (parseArg) is not called on the implied values.
*
* @example
* program
* .addOption(new Option('--log', 'write logging information to file'))
* .addOption(new Option('--trace', 'log extra details').implies({ log: 'trace.txt' }));
*
* @param {Object} impliedOptionValues
* @return {Option}
*/
implies(impliedOptionValues) {
this.implied = Object.assign(this.implied || {}, impliedOptionValues);
return this;
}
/**
* Set environment variable to check for option value.
*
* An environment variable is only used if when processed the current option value is
* undefined, or the source of the current value is 'default' or 'config' or 'env'.
*
* @param {string} name
* @return {Option}
*/
env(name) {
this.envVar = name;
return this;
}
/**
* Set the custom handler for processing CLI option arguments into option values.
*
* @param {Function} [fn]
* @return {Option}
*/
argParser(fn) {
this.parseArg = fn;
return this;
}
/**
* Whether the option is mandatory and must have a value after parsing.
*
* @param {boolean} [mandatory=true]
* @return {Option}
*/
makeOptionMandatory(mandatory = true) {
this.mandatory = !!mandatory;
return this;
}
/**
* Hide option in help.
*
* @param {boolean} [hide=true]
* @return {Option}
*/
hideHelp(hide = true) {
this.hidden = !!hide;
return this;
}
/**
* @api private
*/
_concatValue(value, previous) {
if (previous === this.defaultValue || !Array.isArray(previous)) {
return [value];
}
return previous.concat(value);
}
/**
* Only allow option value to be one of choices.
*
* @param {string[]} values
* @return {Option}
*/
choices(values) {
this.argChoices = values.slice();
this.parseArg = (arg, previous) => {
if (!this.argChoices.includes(arg)) {
throw new InvalidArgumentError(`Allowed choices are ${this.argChoices.join(', ')}.`);
}
if (this.variadic) {
return this._concatValue(arg, previous);
}
return arg;
};
return this;
}
/**
* Return option name.
*
* @return {string}
*/
name() {
if (this.long) {
return this.long.replace(/^--/, '');
}
return this.short.replace(/^-/, '');
}
/**
* Return option name, in a camelcase format that can be used
* as a object attribute key.
*
* @return {string}
* @api private
*/
attributeName() {
return camelcase(this.name().replace(/^no-/, ''));
}
/**
* Check if `arg` matches the short or long flag.
*
* @param {string} arg
* @return {boolean}
* @api private
*/
is(arg) {
return this.short === arg || this.long === arg;
}
/**
* Return whether a boolean option.
*
* Options are one of boolean, negated, required argument, or optional argument.
*
* @return {boolean}
* @api private
*/
isBoolean() {
return !this.required && !this.optional && !this.negate;
}
}
/**
* This class is to make it easier to work with dual options, without changing the existing
* implementation. We support separate dual options for separate positive and negative options,
* like `--build` and `--no-build`, which share a single option value. This works nicely for some
* use cases, but is tricky for others where we want separate behaviours despite
* the single shared option value.
*/
class DualOptions {
/**
* @param {Option[]} options
*/
constructor(options) {
this.positiveOptions = new Map();
this.negativeOptions = new Map();
this.dualOptions = new Set();
options.forEach(option => {
if (option.negate) {
this.negativeOptions.set(option.attributeName(), option);
} else {
this.positiveOptions.set(option.attributeName(), option);
}
});
this.negativeOptions.forEach((value, key) => {
if (this.positiveOptions.has(key)) {
this.dualOptions.add(key);
}
});
}
/**
* Did the value come from the option, and not from possible matching dual option?
*
* @param {any} value
* @param {Option} option
* @returns {boolean}
*/
valueFromOption(value, option) {
const optionKey = option.attributeName();
if (!this.dualOptions.has(optionKey)) return true;
// Use the value to deduce if (probably) came from the option.
const preset = this.negativeOptions.get(optionKey).presetArg;
const negativeValue = (preset !== undefined) ? preset : false;
return option.negate === (negativeValue === value);
}
}
/**
* Convert string from kebab-case to camelCase.
*
* @param {string} str
* @return {string}
* @api private
*/
function camelcase(str) {
return str.split('-').reduce((str, word) => {
return str + word[0].toUpperCase() + word.slice(1);
});
}
/**
* Split the short and long flag out of something like '-m,--mixed <value>'
*
* @api private
*/
function splitOptionFlags(flags) {
let shortFlag;
let longFlag;
// Use original very loose parsing to maintain backwards compatibility for now,
// which allowed for example unintended `-sw, --short-word` [sic].
const flagParts = flags.split(/[ |,]+/);
if (flagParts.length > 1 && !/^[[<]/.test(flagParts[1])) shortFlag = flagParts.shift();
longFlag = flagParts.shift();
// Add support for lone short flag without significantly changing parsing!
if (!shortFlag && /^-[^-]$/.test(longFlag)) {
shortFlag = longFlag;
longFlag = undefined;
}
return { shortFlag, longFlag };
}
exports.Option = Option;
exports.splitOptionFlags = splitOptionFlags;
exports.DualOptions = DualOptions;

View file

@ -0,0 +1,100 @@
const maxDistance = 3;
function editDistance(a, b) {
// https://en.wikipedia.org/wiki/DamerauLevenshtein_distance
// Calculating optimal string alignment distance, no substring is edited more than once.
// (Simple implementation.)
// Quick early exit, return worst case.
if (Math.abs(a.length - b.length) > maxDistance) return Math.max(a.length, b.length);
// distance between prefix substrings of a and b
const d = [];
// pure deletions turn a into empty string
for (let i = 0; i <= a.length; i++) {
d[i] = [i];
}
// pure insertions turn empty string into b
for (let j = 0; j <= b.length; j++) {
d[0][j] = j;
}
// fill matrix
for (let j = 1; j <= b.length; j++) {
for (let i = 1; i <= a.length; i++) {
let cost = 1;
if (a[i - 1] === b[j - 1]) {
cost = 0;
} else {
cost = 1;
}
d[i][j] = Math.min(
d[i - 1][j] + 1, // deletion
d[i][j - 1] + 1, // insertion
d[i - 1][j - 1] + cost // substitution
);
// transposition
if (i > 1 && j > 1 && a[i - 1] === b[j - 2] && a[i - 2] === b[j - 1]) {
d[i][j] = Math.min(d[i][j], d[i - 2][j - 2] + 1);
}
}
}
return d[a.length][b.length];
}
/**
* Find close matches, restricted to same number of edits.
*
* @param {string} word
* @param {string[]} candidates
* @returns {string}
*/
function suggestSimilar(word, candidates) {
if (!candidates || candidates.length === 0) return '';
// remove possible duplicates
candidates = Array.from(new Set(candidates));
const searchingOptions = word.startsWith('--');
if (searchingOptions) {
word = word.slice(2);
candidates = candidates.map(candidate => candidate.slice(2));
}
let similar = [];
let bestDistance = maxDistance;
const minSimilarity = 0.4;
candidates.forEach((candidate) => {
if (candidate.length <= 1) return; // no one character guesses
const distance = editDistance(word, candidate);
const length = Math.max(word.length, candidate.length);
const similarity = (length - distance) / length;
if (similarity > minSimilarity) {
if (distance < bestDistance) {
// better edit distance, throw away previous worse matches
bestDistance = distance;
similar = [candidate];
} else if (distance === bestDistance) {
similar.push(candidate);
}
}
});
similar.sort((a, b) => a.localeCompare(b));
if (searchingOptions) {
similar = similar.map(candidate => `--${candidate}`);
}
if (similar.length > 1) {
return `\n(Did you mean one of ${similar.join(', ')}?)`;
}
if (similar.length === 1) {
return `\n(Did you mean ${similar[0]}?)`;
}
return '';
}
exports.suggestSimilar = suggestSimilar;

View file

@ -0,0 +1,16 @@
{
"versions": [
{
"version": "*",
"target": {
"node": "supported"
},
"response": {
"type": "time-permitting"
},
"backing": {
"npm-funding": true
}
}
]
}

View file

@ -0,0 +1,80 @@
{
"name": "commander",
"version": "9.5.0",
"description": "the complete solution for node.js command-line programs",
"keywords": [
"commander",
"command",
"option",
"parser",
"cli",
"argument",
"args",
"argv"
],
"author": "TJ Holowaychuk <tj@vision-media.ca>",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/tj/commander.js.git"
},
"scripts": {
"lint": "npm run lint:javascript && npm run lint:typescript",
"lint:javascript": "eslint index.js esm.mjs \"lib/*.js\" \"tests/**/*.js\"",
"lint:typescript": "eslint typings/*.ts tests/*.ts",
"test": "jest && npm run test-typings",
"test-esm": "node --experimental-modules ./tests/esm-imports-test.mjs",
"test-typings": "tsd",
"typescript-checkJS": "tsc --allowJS --checkJS index.js lib/*.js --noEmit",
"test-all": "npm run test && npm run lint && npm run typescript-checkJS && npm run test-esm"
},
"files": [
"index.js",
"lib/*.js",
"esm.mjs",
"typings/index.d.ts",
"package-support.json"
],
"type": "commonjs",
"main": "./index.js",
"exports": {
".": {
"types": "./typings/index.d.ts",
"require": "./index.js",
"import": "./esm.mjs"
},
"./esm.mjs": "./esm.mjs"
},
"devDependencies": {
"@types/jest": "^28.1.4",
"@types/node": "^16.11.15",
"@typescript-eslint/eslint-plugin": "^5.30.6",
"@typescript-eslint/parser": "^5.30.6",
"eslint": "^8.19.0",
"eslint-config-standard": "^17.0.0",
"eslint-config-standard-with-typescript": "^22.0.0",
"eslint-plugin-import": "^2.25.3",
"eslint-plugin-jest": "^26.5.3",
"eslint-plugin-n": "^15.2.4",
"eslint-plugin-promise": "^6.0.0",
"jest": "^28.1.2",
"ts-jest": "^28.0.5",
"tsd": "^0.22.0",
"typescript": "^4.7.4"
},
"types": "typings/index.d.ts",
"jest": {
"testEnvironment": "node",
"collectCoverage": true,
"transform": {
"^.+\\.tsx?$": "ts-jest"
},
"testPathIgnorePatterns": [
"/node_modules/"
]
},
"engines": {
"node": "^12.20.0 || >=14"
},
"support": true
}

View file

@ -0,0 +1,891 @@
// Type definitions for commander
// Original definitions by: Alan Agius <https://github.com/alan-agius4>, Marcelo Dezem <https://github.com/mdezem>, vvakame <https://github.com/vvakame>, Jules Randolph <https://github.com/sveinburne>
// Using method rather than property for method-signature-style, to document method overloads separately. Allow either.
/* eslint-disable @typescript-eslint/method-signature-style */
/* eslint-disable @typescript-eslint/no-explicit-any */
export class CommanderError extends Error {
code: string;
exitCode: number;
message: string;
nestedError?: string;
/**
* Constructs the CommanderError class
* @param exitCode - suggested exit code which could be used with process.exit
* @param code - an id string representing the error
* @param message - human-readable description of the error
* @constructor
*/
constructor(exitCode: number, code: string, message: string);
}
export class InvalidArgumentError extends CommanderError {
/**
* Constructs the InvalidArgumentError class
* @param message - explanation of why argument is invalid
* @constructor
*/
constructor(message: string);
}
export { InvalidArgumentError as InvalidOptionArgumentError }; // deprecated old name
export interface ErrorOptions { // optional parameter for error()
/** an id string representing the error */
code?: string;
/** suggested exit code which could be used with process.exit */
exitCode?: number;
}
export class Argument {
description: string;
required: boolean;
variadic: boolean;
/**
* Initialize a new command argument with the given name and description.
* The default is that the argument is required, and you can explicitly
* indicate this with <> around the name. Put [] around the name for an optional argument.
*/
constructor(arg: string, description?: string);
/**
* Return argument name.
*/
name(): string;
/**
* Set the default value, and optionally supply the description to be displayed in the help.
*/
default(value: unknown, description?: string): this;
/**
* Set the custom handler for processing CLI command arguments into argument values.
*/
argParser<T>(fn: (value: string, previous: T) => T): this;
/**
* Only allow argument value to be one of choices.
*/
choices(values: readonly string[]): this;
/**
* Make argument required.
*/
argRequired(): this;
/**
* Make argument optional.
*/
argOptional(): this;
}
export class Option {
flags: string;
description: string;
required: boolean; // A value must be supplied when the option is specified.
optional: boolean; // A value is optional when the option is specified.
variadic: boolean;
mandatory: boolean; // The option must have a value after parsing, which usually means it must be specified on command line.
optionFlags: string;
short?: string;
long?: string;
negate: boolean;
defaultValue?: any;
defaultValueDescription?: string;
parseArg?: <T>(value: string, previous: T) => T;
hidden: boolean;
argChoices?: string[];
constructor(flags: string, description?: string);
/**
* Set the default value, and optionally supply the description to be displayed in the help.
*/
default(value: unknown, description?: string): this;
/**
* Preset to use when option used without option-argument, especially optional but also boolean and negated.
* The custom processing (parseArg) is called.
*
* @example
* ```ts
* new Option('--color').default('GREYSCALE').preset('RGB');
* new Option('--donate [amount]').preset('20').argParser(parseFloat);
* ```
*/
preset(arg: unknown): this;
/**
* Add option name(s) that conflict with this option.
* An error will be displayed if conflicting options are found during parsing.
*
* @example
* ```ts
* new Option('--rgb').conflicts('cmyk');
* new Option('--js').conflicts(['ts', 'jsx']);
* ```
*/
conflicts(names: string | string[]): this;
/**
* Specify implied option values for when this option is set and the implied options are not.
*
* The custom processing (parseArg) is not called on the implied values.
*
* @example
* program
* .addOption(new Option('--log', 'write logging information to file'))
* .addOption(new Option('--trace', 'log extra details').implies({ log: 'trace.txt' }));
*/
implies(optionValues: OptionValues): this;
/**
* Set environment variable to check for option value.
*
* An environment variables is only used if when processed the current option value is
* undefined, or the source of the current value is 'default' or 'config' or 'env'.
*/
env(name: string): this;
/**
* Calculate the full description, including defaultValue etc.
*/
fullDescription(): string;
/**
* Set the custom handler for processing CLI option arguments into option values.
*/
argParser<T>(fn: (value: string, previous: T) => T): this;
/**
* Whether the option is mandatory and must have a value after parsing.
*/
makeOptionMandatory(mandatory?: boolean): this;
/**
* Hide option in help.
*/
hideHelp(hide?: boolean): this;
/**
* Only allow option value to be one of choices.
*/
choices(values: readonly string[]): this;
/**
* Return option name.
*/
name(): string;
/**
* Return option name, in a camelcase format that can be used
* as a object attribute key.
*/
attributeName(): string;
/**
* Return whether a boolean option.
*
* Options are one of boolean, negated, required argument, or optional argument.
*/
isBoolean(): boolean;
}
export class Help {
/** output helpWidth, long lines are wrapped to fit */
helpWidth?: number;
sortSubcommands: boolean;
sortOptions: boolean;
showGlobalOptions: boolean;
constructor();
/** Get the command term to show in the list of subcommands. */
subcommandTerm(cmd: Command): string;
/** Get the command summary to show in the list of subcommands. */
subcommandDescription(cmd: Command): string;
/** Get the option term to show in the list of options. */
optionTerm(option: Option): string;
/** Get the option description to show in the list of options. */
optionDescription(option: Option): string;
/** Get the argument term to show in the list of arguments. */
argumentTerm(argument: Argument): string;
/** Get the argument description to show in the list of arguments. */
argumentDescription(argument: Argument): string;
/** Get the command usage to be displayed at the top of the built-in help. */
commandUsage(cmd: Command): string;
/** Get the description for the command. */
commandDescription(cmd: Command): string;
/** Get an array of the visible subcommands. Includes a placeholder for the implicit help command, if there is one. */
visibleCommands(cmd: Command): Command[];
/** Get an array of the visible options. Includes a placeholder for the implicit help option, if there is one. */
visibleOptions(cmd: Command): Option[];
/** Get an array of the visible global options. (Not including help.) */
visibleGlobalOptions(cmd: Command): Option[];
/** Get an array of the arguments which have descriptions. */
visibleArguments(cmd: Command): Argument[];
/** Get the longest command term length. */
longestSubcommandTermLength(cmd: Command, helper: Help): number;
/** Get the longest option term length. */
longestOptionTermLength(cmd: Command, helper: Help): number;
/** Get the longest global option term length. */
longestGlobalOptionTermLength(cmd: Command, helper: Help): number;
/** Get the longest argument term length. */
longestArgumentTermLength(cmd: Command, helper: Help): number;
/** Calculate the pad width from the maximum term length. */
padWidth(cmd: Command, helper: Help): number;
/**
* Wrap the given string to width characters per line, with lines after the first indented.
* Do not wrap if insufficient room for wrapping (minColumnWidth), or string is manually formatted.
*/
wrap(str: string, width: number, indent: number, minColumnWidth?: number): string;
/** Generate the built-in help text. */
formatHelp(cmd: Command, helper: Help): string;
}
export type HelpConfiguration = Partial<Help>;
export interface ParseOptions {
from: 'node' | 'electron' | 'user';
}
export interface HelpContext { // optional parameter for .help() and .outputHelp()
error: boolean;
}
export interface AddHelpTextContext { // passed to text function used with .addHelpText()
error: boolean;
command: Command;
}
export interface OutputConfiguration {
writeOut?(str: string): void;
writeErr?(str: string): void;
getOutHelpWidth?(): number;
getErrHelpWidth?(): number;
outputError?(str: string, write: (str: string) => void): void;
}
export type AddHelpTextPosition = 'beforeAll' | 'before' | 'after' | 'afterAll';
export type HookEvent = 'preSubcommand' | 'preAction' | 'postAction';
export type OptionValueSource = 'default' | 'config' | 'env' | 'cli' | 'implied';
export interface OptionValues {
[key: string]: any;
}
export class Command {
args: string[];
processedArgs: any[];
commands: Command[];
parent: Command | null;
constructor(name?: string);
/**
* Set the program version to `str`.
*
* This method auto-registers the "-V, --version" flag
* which will print the version number when passed.
*
* You can optionally supply the flags and description to override the defaults.
*/
version(str: string, flags?: string, description?: string): this;
/**
* Define a command, implemented using an action handler.
*
* @remarks
* The command description is supplied using `.description`, not as a parameter to `.command`.
*
* @example
* ```ts
* program
* .command('clone <source> [destination]')
* .description('clone a repository into a newly created directory')
* .action((source, destination) => {
* console.log('clone command called');
* });
* ```
*
* @param nameAndArgs - command name and arguments, args are `<required>` or `[optional]` and last may also be `variadic...`
* @param opts - configuration options
* @returns new command
*/
command(nameAndArgs: string, opts?: CommandOptions): ReturnType<this['createCommand']>;
/**
* Define a command, implemented in a separate executable file.
*
* @remarks
* The command description is supplied as the second parameter to `.command`.
*
* @example
* ```ts
* program
* .command('start <service>', 'start named service')
* .command('stop [service]', 'stop named service, or all if no name supplied');
* ```
*
* @param nameAndArgs - command name and arguments, args are `<required>` or `[optional]` and last may also be `variadic...`
* @param description - description of executable command
* @param opts - configuration options
* @returns `this` command for chaining
*/
command(nameAndArgs: string, description: string, opts?: ExecutableCommandOptions): this;
/**
* Factory routine to create a new unattached command.
*
* See .command() for creating an attached subcommand, which uses this routine to
* create the command. You can override createCommand to customise subcommands.
*/
createCommand(name?: string): Command;
/**
* Add a prepared subcommand.
*
* See .command() for creating an attached subcommand which inherits settings from its parent.
*
* @returns `this` command for chaining
*/
addCommand(cmd: Command, opts?: CommandOptions): this;
/**
* Factory routine to create a new unattached argument.
*
* See .argument() for creating an attached argument, which uses this routine to
* create the argument. You can override createArgument to return a custom argument.
*/
createArgument(name: string, description?: string): Argument;
/**
* Define argument syntax for command.
*
* The default is that the argument is required, and you can explicitly
* indicate this with <> around the name. Put [] around the name for an optional argument.
*
* @example
* ```
* program.argument('<input-file>');
* program.argument('[output-file]');
* ```
*
* @returns `this` command for chaining
*/
argument<T>(flags: string, description: string, fn: (value: string, previous: T) => T, defaultValue?: T): this;
argument(name: string, description?: string, defaultValue?: unknown): this;
/**
* Define argument syntax for command, adding a prepared argument.
*
* @returns `this` command for chaining
*/
addArgument(arg: Argument): this;
/**
* Define argument syntax for command, adding multiple at once (without descriptions).
*
* See also .argument().
*
* @example
* ```
* program.arguments('<cmd> [env]');
* ```
*
* @returns `this` command for chaining
*/
arguments(names: string): this;
/**
* Override default decision whether to add implicit help command.
*
* @example
* ```
* addHelpCommand() // force on
* addHelpCommand(false); // force off
* addHelpCommand('help [cmd]', 'display help for [cmd]'); // force on with custom details
* ```
*
* @returns `this` command for chaining
*/
addHelpCommand(enableOrNameAndArgs?: string | boolean, description?: string): this;
/**
* Add hook for life cycle event.
*/
hook(event: HookEvent, listener: (thisCommand: Command, actionCommand: Command) => void | Promise<void>): this;
/**
* Register callback to use as replacement for calling process.exit.
*/
exitOverride(callback?: (err: CommanderError) => never | void): this;
/**
* Display error message and exit (or call exitOverride).
*/
error(message: string, errorOptions?: ErrorOptions): never;
/**
* You can customise the help with a subclass of Help by overriding createHelp,
* or by overriding Help properties using configureHelp().
*/
createHelp(): Help;
/**
* You can customise the help by overriding Help properties using configureHelp(),
* or with a subclass of Help by overriding createHelp().
*/
configureHelp(configuration: HelpConfiguration): this;
/** Get configuration */
configureHelp(): HelpConfiguration;
/**
* The default output goes to stdout and stderr. You can customise this for special
* applications. You can also customise the display of errors by overriding outputError.
*
* The configuration properties are all functions:
* ```
* // functions to change where being written, stdout and stderr
* writeOut(str)
* writeErr(str)
* // matching functions to specify width for wrapping help
* getOutHelpWidth()
* getErrHelpWidth()
* // functions based on what is being written out
* outputError(str, write) // used for displaying errors, and not used for displaying help
* ```
*/
configureOutput(configuration: OutputConfiguration): this;
/** Get configuration */
configureOutput(): OutputConfiguration;
/**
* Copy settings that are useful to have in common across root command and subcommands.
*
* (Used internally when adding a command using `.command()` so subcommands inherit parent settings.)
*/
copyInheritedSettings(sourceCommand: Command): this;
/**
* Display the help or a custom message after an error occurs.
*/
showHelpAfterError(displayHelp?: boolean | string): this;
/**
* Display suggestion of similar commands for unknown commands, or options for unknown options.
*/
showSuggestionAfterError(displaySuggestion?: boolean): this;
/**
* Register callback `fn` for the command.
*
* @example
* ```
* program
* .command('serve')
* .description('start service')
* .action(function() {
* // do work here
* });
* ```
*
* @returns `this` command for chaining
*/
action(fn: (...args: any[]) => void | Promise<void>): this;
/**
* Define option with `flags`, `description` and optional
* coercion `fn`.
*
* The `flags` string contains the short and/or long flags,
* separated by comma, a pipe or space. The following are all valid
* all will output this way when `--help` is used.
*
* "-p, --pepper"
* "-p|--pepper"
* "-p --pepper"
*
* @example
* ```
* // simple boolean defaulting to false
* program.option('-p, --pepper', 'add pepper');
*
* --pepper
* program.pepper
* // => Boolean
*
* // simple boolean defaulting to true
* program.option('-C, --no-cheese', 'remove cheese');
*
* program.cheese
* // => true
*
* --no-cheese
* program.cheese
* // => false
*
* // required argument
* program.option('-C, --chdir <path>', 'change the working directory');
*
* --chdir /tmp
* program.chdir
* // => "/tmp"
*
* // optional argument
* program.option('-c, --cheese [type]', 'add cheese [marble]');
* ```
*
* @returns `this` command for chaining
*/
option(flags: string, description?: string, defaultValue?: string | boolean | string[]): this;
option<T>(flags: string, description: string, fn: (value: string, previous: T) => T, defaultValue?: T): this;
/** @deprecated since v7, instead use choices or a custom function */
option(flags: string, description: string, regexp: RegExp, defaultValue?: string | boolean | string[]): this;
/**
* Define a required option, which must have a value after parsing. This usually means
* the option must be specified on the command line. (Otherwise the same as .option().)
*
* The `flags` string contains the short and/or long flags, separated by comma, a pipe or space.
*/
requiredOption(flags: string, description?: string, defaultValue?: string | boolean | string[]): this;
requiredOption<T>(flags: string, description: string, fn: (value: string, previous: T) => T, defaultValue?: T): this;
/** @deprecated since v7, instead use choices or a custom function */
requiredOption(flags: string, description: string, regexp: RegExp, defaultValue?: string | boolean | string[]): this;
/**
* Factory routine to create a new unattached option.
*
* See .option() for creating an attached option, which uses this routine to
* create the option. You can override createOption to return a custom option.
*/
createOption(flags: string, description?: string): Option;
/**
* Add a prepared Option.
*
* See .option() and .requiredOption() for creating and attaching an option in a single call.
*/
addOption(option: Option): this;
/**
* Whether to store option values as properties on command object,
* or store separately (specify false). In both cases the option values can be accessed using .opts().
*
* @returns `this` command for chaining
*/
storeOptionsAsProperties<T extends OptionValues>(): this & T;
storeOptionsAsProperties<T extends OptionValues>(storeAsProperties: true): this & T;
storeOptionsAsProperties(storeAsProperties?: boolean): this;
/**
* Retrieve option value.
*/
getOptionValue(key: string): any;
/**
* Store option value.
*/
setOptionValue(key: string, value: unknown): this;
/**
* Store option value and where the value came from.
*/
setOptionValueWithSource(key: string, value: unknown, source: OptionValueSource): this;
/**
* Get source of option value.
*/
getOptionValueSource(key: string): OptionValueSource | undefined;
/**
* Get source of option value. See also .optsWithGlobals().
*/
getOptionValueSourceWithGlobals(key: string): OptionValueSource | undefined;
/**
* Alter parsing of short flags with optional values.
*
* @example
* ```
* // for `.option('-f,--flag [value]'):
* .combineFlagAndOptionalValue(true) // `-f80` is treated like `--flag=80`, this is the default behaviour
* .combineFlagAndOptionalValue(false) // `-fb` is treated like `-f -b`
* ```
*
* @returns `this` command for chaining
*/
combineFlagAndOptionalValue(combine?: boolean): this;
/**
* Allow unknown options on the command line.
*
* @returns `this` command for chaining
*/
allowUnknownOption(allowUnknown?: boolean): this;
/**
* Allow excess command-arguments on the command line. Pass false to make excess arguments an error.
*
* @returns `this` command for chaining
*/
allowExcessArguments(allowExcess?: boolean): this;
/**
* Enable positional options. Positional means global options are specified before subcommands which lets
* subcommands reuse the same option names, and also enables subcommands to turn on passThroughOptions.
*
* The default behaviour is non-positional and global options may appear anywhere on the command line.
*
* @returns `this` command for chaining
*/
enablePositionalOptions(positional?: boolean): this;
/**
* Pass through options that come after command-arguments rather than treat them as command-options,
* so actual command-options come before command-arguments. Turning this on for a subcommand requires
* positional options to have been enabled on the program (parent commands).
*
* The default behaviour is non-positional and options may appear before or after command-arguments.
*
* @returns `this` command for chaining
*/
passThroughOptions(passThrough?: boolean): this;
/**
* Parse `argv`, setting options and invoking commands when defined.
*
* The default expectation is that the arguments are from node and have the application as argv[0]
* and the script being run in argv[1], with user parameters after that.
*
* @example
* ```
* program.parse(process.argv);
* program.parse(); // implicitly use process.argv and auto-detect node vs electron conventions
* program.parse(my-args, { from: 'user' }); // just user supplied arguments, nothing special about argv[0]
* ```
*
* @returns `this` command for chaining
*/
parse(argv?: readonly string[], options?: ParseOptions): this;
/**
* Parse `argv`, setting options and invoking commands when defined.
*
* Use parseAsync instead of parse if any of your action handlers are async. Returns a Promise.
*
* The default expectation is that the arguments are from node and have the application as argv[0]
* and the script being run in argv[1], with user parameters after that.
*
* @example
* ```
* program.parseAsync(process.argv);
* program.parseAsync(); // implicitly use process.argv and auto-detect node vs electron conventions
* program.parseAsync(my-args, { from: 'user' }); // just user supplied arguments, nothing special about argv[0]
* ```
*
* @returns Promise
*/
parseAsync(argv?: readonly string[], options?: ParseOptions): Promise<this>;
/**
* Parse options from `argv` removing known options,
* and return argv split into operands and unknown arguments.
*
* argv => operands, unknown
* --known kkk op => [op], []
* op --known kkk => [op], []
* sub --unknown uuu op => [sub], [--unknown uuu op]
* sub -- --unknown uuu op => [sub --unknown uuu op], []
*/
parseOptions(argv: string[]): ParseOptionsResult;
/**
* Return an object containing local option values as key-value pairs
*/
opts<T extends OptionValues>(): T;
/**
* Return an object containing merged local and global option values as key-value pairs.
*/
optsWithGlobals<T extends OptionValues>(): T;
/**
* Set the description.
*
* @returns `this` command for chaining
*/
description(str: string): this;
/** @deprecated since v8, instead use .argument to add command argument with description */
description(str: string, argsDescription: {[argName: string]: string}): this;
/**
* Get the description.
*/
description(): string;
/**
* Set the summary. Used when listed as subcommand of parent.
*
* @returns `this` command for chaining
*/
summary(str: string): this;
/**
* Get the summary.
*/
summary(): string;
/**
* Set an alias for the command.
*
* You may call more than once to add multiple aliases. Only the first alias is shown in the auto-generated help.
*
* @returns `this` command for chaining
*/
alias(alias: string): this;
/**
* Get alias for the command.
*/
alias(): string;
/**
* Set aliases for the command.
*
* Only the first alias is shown in the auto-generated help.
*
* @returns `this` command for chaining
*/
aliases(aliases: readonly string[]): this;
/**
* Get aliases for the command.
*/
aliases(): string[];
/**
* Set the command usage.
*
* @returns `this` command for chaining
*/
usage(str: string): this;
/**
* Get the command usage.
*/
usage(): string;
/**
* Set the name of the command.
*
* @returns `this` command for chaining
*/
name(str: string): this;
/**
* Get the name of the command.
*/
name(): string;
/**
* Set the name of the command from script filename, such as process.argv[1],
* or require.main.filename, or __filename.
*
* (Used internally and public although not documented in README.)
*
* @example
* ```ts
* program.nameFromFilename(require.main.filename);
* ```
*
* @returns `this` command for chaining
*/
nameFromFilename(filename: string): this;
/**
* Set the directory for searching for executable subcommands of this command.
*
* @example
* ```ts
* program.executableDir(__dirname);
* // or
* program.executableDir('subcommands');
* ```
*
* @returns `this` command for chaining
*/
executableDir(path: string): this;
/**
* Get the executable search directory.
*/
executableDir(): string;
/**
* Output help information for this command.
*
* Outputs built-in help, and custom text added using `.addHelpText()`.
*
*/
outputHelp(context?: HelpContext): void;
/** @deprecated since v7 */
outputHelp(cb?: (str: string) => string): void;
/**
* Return command help documentation.
*/
helpInformation(context?: HelpContext): string;
/**
* You can pass in flags and a description to override the help
* flags and help description for your command. Pass in false
* to disable the built-in help option.
*/
helpOption(flags?: string | boolean, description?: string): this;
/**
* Output help information and exit.
*
* Outputs built-in help, and custom text added using `.addHelpText()`.
*/
help(context?: HelpContext): never;
/** @deprecated since v7 */
help(cb?: (str: string) => string): never;
/**
* Add additional text to be displayed with the built-in help.
*
* Position is 'before' or 'after' to affect just this command,
* and 'beforeAll' or 'afterAll' to affect this command and all its subcommands.
*/
addHelpText(position: AddHelpTextPosition, text: string): this;
addHelpText(position: AddHelpTextPosition, text: (context: AddHelpTextContext) => string): this;
/**
* Add a listener (callback) for when events occur. (Implemented using EventEmitter.)
*/
on(event: string | symbol, listener: (...args: any[]) => void): this;
}
export interface CommandOptions {
hidden?: boolean;
isDefault?: boolean;
/** @deprecated since v7, replaced by hidden */
noHelp?: boolean;
}
export interface ExecutableCommandOptions extends CommandOptions {
executableFile?: string;
}
export interface ParseOptionsResult {
operands: string[];
unknown: string[];
}
export function createCommand(name?: string): Command;
export function createOption(flags: string, description?: string): Option;
export function createArgument(name: string, description?: string): Argument;
export const program: Command;

View file

@ -0,0 +1,88 @@
{
"name": "@capacitor/cli",
"version": "5.7.8",
"description": "Capacitor: Cross-platform apps with JavaScript and the web",
"homepage": "https://capacitorjs.com",
"author": "Ionic Team <hi@ionic.io> (https://ionic.io)",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/ionic-team/capacitor.git"
},
"bugs": {
"url": "https://github.com/ionic-team/capacitor/issues"
},
"files": [
"assets/",
"bin/",
"dist/**/*.js",
"dist/declarations.d.ts"
],
"keywords": [
"ionic",
"ionic framework",
"capacitor",
"universal app",
"progressive web apps",
"cross platform"
],
"engines": {
"node": ">=16.0.0"
},
"main": "dist/index.js",
"types": "dist/declarations.d.ts",
"bin": {
"capacitor": "./bin/capacitor",
"cap": "./bin/capacitor"
},
"scripts": {
"build": "npm run clean && npm run assets && tsc",
"clean": "rimraf ./dist",
"assets": "node ../scripts/pack-cli-assets.mjs",
"prepublishOnly": "npm run build",
"test": "jest -i",
"watch": "npm run assets && tsc -w"
},
"dependencies": {
"@ionic/cli-framework-output": "^2.2.5",
"@ionic/utils-fs": "^3.1.6",
"@ionic/utils-subprocess": "^2.1.11",
"@ionic/utils-terminal": "^2.3.3",
"commander": "^9.3.0",
"debug": "^4.3.4",
"env-paths": "^2.2.0",
"kleur": "^4.1.4",
"native-run": "^2.0.0",
"open": "^8.4.0",
"plist": "^3.0.5",
"prompts": "^2.4.2",
"rimraf": "^4.4.1",
"semver": "^7.3.7",
"tar": "^6.1.11",
"tslib": "^2.4.0",
"xml2js": "^0.5.0"
},
"devDependencies": {
"@types/debug": "^4.1.7",
"@types/jest": "^29.5.0",
"@types/plist": "^3.0.2",
"@types/prompts": "^2.0.14",
"@types/semver": "^7.3.10",
"@types/tar": "^6.1.1",
"@types/tmp": "^0.2.3",
"@types/xml2js": "0.4.5",
"jest": "^29.5.0",
"jest-environment-jsdom": "^29.5.0",
"jest-jasmine2": "^29.5.0",
"tmp": "^0.2.1",
"ts-jest": "^29.0.5",
"typescript": "~5.0.2"
},
"jest": {
"preset": "ts-jest",
"testRunner": "jest-jasmine2"
},
"publishConfig": {
"access": "public"
}
}

View file

@ -0,0 +1,185 @@
# Change Log
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [2.1.6](https://github.com/ionic-team/ionic-cli/compare/@ionic/utils-array@2.1.5...@ionic/utils-array@2.1.6) (2023-03-29)
**Note:** Version bump only for package @ionic/utils-array
## [2.1.5](https://github.com/ionic-team/ionic-cli/compare/@ionic/utils-array@2.1.4...@ionic/utils-array@2.1.5) (2020-08-28)
**Note:** Version bump only for package @ionic/utils-array
## [2.1.4](https://github.com/ionic-team/ionic-cli/compare/@ionic/utils-array@2.1.3...@ionic/utils-array@2.1.4) (2020-08-25)
**Note:** Version bump only for package @ionic/utils-array
## [2.1.3](https://github.com/ionic-team/ionic-cli/compare/@ionic/utils-array@2.1.2...@ionic/utils-array@2.1.3) (2020-05-12)
### Bug Fixes
* pin tslib to avoid "Cannot set property pathExists" error ([689e1f0](https://github.com/ionic-team/ionic-cli/commit/689e1f038b907356ef855a067a76d4822e7072a8))
## [2.1.2](https://github.com/ionic-team/ionic-cli/compare/@ionic/utils-array@2.1.1...@ionic/utils-array@2.1.2) (2020-05-06)
**Note:** Version bump only for package @ionic/utils-array
## [2.1.1](https://github.com/ionic-team/ionic-cli/compare/@ionic/utils-array@2.1.0...@ionic/utils-array@2.1.1) (2020-03-03)
**Note:** Version bump only for package @ionic/utils-array
# 2.1.0 (2020-02-11)
### Features
* **start:** add new list starter option ([#4315](https://github.com/ionic-team/ionic-cli/issues/4315)) ([1df44c1](https://github.com/ionic-team/ionic-cli/commit/1df44c1591f37b89f2b672857740edd6cb2aea67))
## [2.0.2](https://github.com/ionic-team/ionic-cli/compare/@ionic/utils-array@2.0.1...@ionic/utils-array@2.0.2) (2020-02-10)
**Note:** Version bump only for package @ionic/utils-array
## [2.0.1](https://github.com/ionic-team/ionic-cli/compare/@ionic/utils-array@2.0.0...@ionic/utils-array@2.0.1) (2020-02-03)
**Note:** Version bump only for package @ionic/utils-array
# [2.0.0](https://github.com/ionic-team/ionic-cli/compare/@ionic/utils-array@1.2.2...@ionic/utils-array@2.0.0) (2020-01-25)
### chore
* require Node 10 ([5a47874](https://github.com/ionic-team/ionic-cli/commit/5a478746c074207b6dc96aa8771f04a606deb1ef))
### BREAKING CHANGES
* A minimum of Node.js 10.3.0 is required.
## [1.2.2](https://github.com/ionic-team/ionic-cli/compare/@ionic/utils-array@1.2.1...@ionic/utils-array@1.2.2) (2019-12-05)
**Note:** Version bump only for package @ionic/utils-array
## [1.2.1](https://github.com/ionic-team/ionic-cli/compare/@ionic/utils-array@1.2.0...@ionic/utils-array@1.2.1) (2019-09-18)
**Note:** Version bump only for package @ionic/utils-array
# [1.2.0](https://github.com/ionic-team/ionic-cli/compare/@ionic/utils-array@1.1.1...@ionic/utils-array@1.2.0) (2019-08-28)
### Features
* **replace:** add replace item in array by index function ([011ddf7](https://github.com/ionic-team/ionic-cli/commit/011ddf7))
* **splice:** add non-mutating splice function ([758d287](https://github.com/ionic-team/ionic-cli/commit/758d287))
## [1.1.1](https://github.com/ionic-team/ionic-cli/compare/@ionic/utils-array@1.1.0...@ionic/utils-array@1.1.1) (2019-08-23)
**Note:** Version bump only for package @ionic/utils-array
# [1.1.0](https://github.com/ionic-team/ionic-cli/compare/@ionic/utils-array@1.0.2...@ionic/utils-array@1.1.0) (2019-08-14)
### Features
* add new `move` function ([ba8da3b](https://github.com/ionic-team/ionic-cli/commit/ba8da3b))
## [1.0.2](https://github.com/ionic-team/ionic-cli/compare/@ionic/utils-array@1.0.1...@ionic/utils-array@1.0.2) (2019-08-07)
**Note:** Version bump only for package @ionic/utils-array
## [1.0.1](https://github.com/ionic-team/ionic-cli/compare/@ionic/utils-array@1.0.0...@ionic/utils-array@1.0.1) (2019-06-05)
**Note:** Version bump only for package @ionic/utils-array
# [1.0.0](https://github.com/ionic-team/ionic-cli/compare/@ionic/utils-array@0.0.1...@ionic/utils-array@1.0.0) (2019-05-29)
### chore
* require Node 8 ([5670e68](https://github.com/ionic-team/ionic-cli/commit/5670e68))
### BREAKING CHANGES
* A minimum of Node.js 8.9.4 is required.
<a name="0.0.1"></a>
## 0.0.1 (2019-02-27)
**Note:** Version bump only for package @ionic/utils-array

View file

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2017 Drifty Co
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -0,0 +1 @@
# @ionic/utils-array

View file

@ -0,0 +1,39 @@
export declare function conform<T>(t?: T | T[]): T[];
export declare function concurrentFilter<T>(array: T[], callback: (currentValue: T) => Promise<boolean>): Promise<T[]>;
export declare function filter<T>(array: T[], callback: (currentValue: T, currentIndex: number, array: readonly T[]) => Promise<boolean>): Promise<T[]>;
export declare function filter<T>(array: readonly T[], callback: (currentValue: T, currentIndex: number, array: readonly T[]) => Promise<boolean>): Promise<readonly T[]>;
export declare function map<T, U>(array: T[], callback: (currentValue: T, currentIndex: number, array: readonly T[]) => Promise<U>): Promise<U[]>;
export declare function map<T, U>(array: T[], callback: (currentValue: T, currentIndex: number, array: readonly T[]) => Promise<U>): Promise<readonly U[]>;
export declare function map<T, U>(array: readonly T[], callback: (currentValue: T, currentIndex: number, array: readonly T[]) => Promise<U>): Promise<U[]>;
export declare function map<T, U>(array: readonly T[], callback: (currentValue: T, currentIndex: number, array: readonly T[]) => Promise<U>): Promise<readonly U[]>;
export declare function reduce<T>(array: T[], callback: (accumulator: T, currentValue: T, currentIndex: number, array: readonly T[]) => Promise<T>): Promise<T>;
export declare function reduce<T>(array: T[], callback: (accumulator: T, currentValue: T, currentIndex: number, array: readonly T[]) => Promise<T>, initialValue: T): Promise<T>;
export declare function reduce<T, R>(array: T[], callback: (accumulator: R, currentValue: T, currentIndex: number, array: readonly T[]) => Promise<R>): Promise<R>;
export declare function reduce<T, U>(array: T[], callback: (accumulator: U, currentValue: T, currentIndex: number, array: readonly T[]) => Promise<U>, initialValue: U): Promise<U>;
export declare function reduce<T>(array: readonly T[], callback: (accumulator: T, currentValue: T, currentIndex: number, array: readonly T[]) => Promise<T>): Promise<T>;
export declare function reduce<T>(array: readonly T[], callback: (accumulator: T, currentValue: T, currentIndex: number, array: readonly T[]) => Promise<T>, initialValue: T): Promise<T>;
export declare function reduce<T, R>(array: readonly T[], callback: (accumulator: R, currentValue: T, currentIndex: number, array: readonly T[]) => Promise<R>): Promise<R>;
export declare function reduce<T, U>(array: readonly T[], callback: (accumulator: U, currentValue: T, currentIndex: number, array: readonly T[]) => Promise<U>, initialValue: U): Promise<U>;
/**
* Splice an array.
*
* This function will return a new array with the standard splice behavior
* applied. Unlike the standard array splice, the array of removed items is not
* returned.
*/
export declare function splice<T>(array: readonly T[], start: number, deleteCount?: number, ...items: T[]): T[];
/**
* Move an item in an array by index.
*
* This function will return a new array with the item in the `fromIndex`
* position moved to the `toIndex` position. If `fromIndex` or `toIndex` are
* out of bounds, the array items remain unmoved.
*/
export declare function move<T>(array: readonly T[], fromIndex: number, toIndex: number): T[];
/**
* Replace an item in an array by index.
*
* This function will return a new array with the item in the `index` position
* replaced with `item`. If `index` is out of bounds, the item is not replaced.
*/
export declare function replace<T>(array: readonly T[], index: number, item: T): T[];

View file

@ -0,0 +1,97 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.replace = exports.move = exports.splice = exports.reduce = exports.map = exports.filter = exports.concurrentFilter = exports.conform = void 0;
function conform(t) {
if (typeof t === 'undefined') {
return [];
}
if (!Array.isArray(t)) {
return [t];
}
return t;
}
exports.conform = conform;
async function concurrentFilter(array, callback) {
const mapper = async (v) => [v, await callback(v)];
const mapped = await Promise.all(array.map(mapper));
return mapped
.filter(([, f]) => f)
.map(([v]) => v);
}
exports.concurrentFilter = concurrentFilter;
async function filter(array, callback) {
const initial = [];
return reduce(array, async (acc, v, i, arr) => {
if (await callback(v, i, arr)) {
acc.push(v);
}
return acc;
}, initial);
}
exports.filter = filter;
async function map(array, callback) {
const initial = [];
return reduce(array, async (acc, v, i, arr) => {
acc.push(await callback(v, i, arr));
return acc;
}, initial);
}
exports.map = map;
async function reduce(array, callback, initialValue) {
const hadInitialValue = typeof initialValue === 'undefined';
const startingIndex = hadInitialValue ? 1 : 0;
if (typeof initialValue === 'undefined') {
if (array.length === 0) {
throw new TypeError('Reduce of empty array with no initial value');
}
initialValue = array[0];
}
let value = initialValue;
for (let i = startingIndex; i < array.length; i++) {
const v = await callback(value, array[i], i, array);
value = v;
}
return value;
}
exports.reduce = reduce;
/**
* Splice an array.
*
* This function will return a new array with the standard splice behavior
* applied. Unlike the standard array splice, the array of removed items is not
* returned.
*/
function splice(array, start, deleteCount = array.length - start, ...items) {
const result = [...array];
result.splice(start, deleteCount, ...items);
return result;
}
exports.splice = splice;
/**
* Move an item in an array by index.
*
* This function will return a new array with the item in the `fromIndex`
* position moved to the `toIndex` position. If `fromIndex` or `toIndex` are
* out of bounds, the array items remain unmoved.
*/
function move(array, fromIndex, toIndex) {
const element = array[fromIndex];
if (fromIndex < 0 || toIndex < 0 || fromIndex >= array.length || toIndex >= array.length) {
return [...array];
}
return splice(splice(array, fromIndex, 1), toIndex, 0, element);
}
exports.move = move;
/**
* Replace an item in an array by index.
*
* This function will return a new array with the item in the `index` position
* replaced with `item`. If `index` is out of bounds, the item is not replaced.
*/
function replace(array, index, item) {
if (index < 0 || index > array.length) {
return [...array];
}
return splice(array, index, 1, item);
}
exports.replace = replace;

View file

@ -0,0 +1,49 @@
{
"name": "@ionic/utils-array",
"version": "2.1.6",
"description": "Array utils",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"homepage": "https://ionicframework.com/",
"author": "Ionic Team <hi@ionic.io> (https://ionic.io)",
"license": "MIT",
"engines": {
"node": ">=16.0.0"
},
"files": [
"dist/",
"LICENSE",
"README.md"
],
"repository": {
"type": "git",
"url": "https://github.com/ionic-team/ionic-cli.git"
},
"bugs": {
"url": "https://github.com/ionic-team/ionic-cli/issues"
},
"scripts": {
"clean": "rimraf dist",
"lint": "true",
"build": "npm run clean && tsc",
"watch": "tsc -w --preserveWatchOutput",
"test": "jest --maxWorkers=4",
"prepublishOnly": "npm run build"
},
"dependencies": {
"debug": "^4.0.0",
"tslib": "^2.0.1"
},
"devDependencies": {
"@types/debug": "^4.1.1",
"@types/jest": "^26.0.10",
"@types/node": "~16.0.0",
"jest": "^26.4.2",
"jest-cli": "^26.0.1",
"lint-staged": "^10.0.2",
"rimraf": "^3.0.0",
"ts-jest": "~26.3.0",
"typescript": "~4.8.0"
},
"gitHead": "15ef6e7da4eace4fd55d16fd9508d156a4bc8203"
}

View file

@ -0,0 +1,2 @@
tidelift: "npm/brace-expansion"
patreon: juliangruber

21
@capacitor/assets/node_modules/brace-expansion/LICENSE generated vendored Normal file
View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2013 Julian Gruber <julian@juliangruber.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -0,0 +1,135 @@
# brace-expansion
[Brace expansion](https://www.gnu.org/software/bash/manual/html_node/Brace-Expansion.html),
as known from sh/bash, in JavaScript.
[![build status](https://secure.travis-ci.org/juliangruber/brace-expansion.svg)](http://travis-ci.org/juliangruber/brace-expansion)
[![downloads](https://img.shields.io/npm/dm/brace-expansion.svg)](https://www.npmjs.org/package/brace-expansion)
[![Greenkeeper badge](https://badges.greenkeeper.io/juliangruber/brace-expansion.svg)](https://greenkeeper.io/)
[![testling badge](https://ci.testling.com/juliangruber/brace-expansion.png)](https://ci.testling.com/juliangruber/brace-expansion)
## Example
```js
var expand = require('brace-expansion');
expand('file-{a,b,c}.jpg')
// => ['file-a.jpg', 'file-b.jpg', 'file-c.jpg']
expand('-v{,,}')
// => ['-v', '-v', '-v']
expand('file{0..2}.jpg')
// => ['file0.jpg', 'file1.jpg', 'file2.jpg']
expand('file-{a..c}.jpg')
// => ['file-a.jpg', 'file-b.jpg', 'file-c.jpg']
expand('file{2..0}.jpg')
// => ['file2.jpg', 'file1.jpg', 'file0.jpg']
expand('file{0..4..2}.jpg')
// => ['file0.jpg', 'file2.jpg', 'file4.jpg']
expand('file-{a..e..2}.jpg')
// => ['file-a.jpg', 'file-c.jpg', 'file-e.jpg']
expand('file{00..10..5}.jpg')
// => ['file00.jpg', 'file05.jpg', 'file10.jpg']
expand('{{A..C},{a..c}}')
// => ['A', 'B', 'C', 'a', 'b', 'c']
expand('ppp{,config,oe{,conf}}')
// => ['ppp', 'pppconfig', 'pppoe', 'pppoeconf']
```
## API
```js
var expand = require('brace-expansion');
```
### var expanded = expand(str)
Return an array of all possible and valid expansions of `str`. If none are
found, `[str]` is returned.
Valid expansions are:
```js
/^(.*,)+(.+)?$/
// {a,b,...}
```
A comma separated list of options, like `{a,b}` or `{a,{b,c}}` or `{,a,}`.
```js
/^-?\d+\.\.-?\d+(\.\.-?\d+)?$/
// {x..y[..incr]}
```
A numeric sequence from `x` to `y` inclusive, with optional increment.
If `x` or `y` start with a leading `0`, all the numbers will be padded
to have equal length. Negative numbers and backwards iteration work too.
```js
/^-?\d+\.\.-?\d+(\.\.-?\d+)?$/
// {x..y[..incr]}
```
An alphabetic sequence from `x` to `y` inclusive, with optional increment.
`x` and `y` must be exactly one character, and if given, `incr` must be a
number.
For compatibility reasons, the string `${` is not eligible for brace expansion.
## Installation
With [npm](https://npmjs.org) do:
```bash
npm install brace-expansion
```
## Contributors
- [Julian Gruber](https://github.com/juliangruber)
- [Isaac Z. Schlueter](https://github.com/isaacs)
## Sponsors
This module is proudly supported by my [Sponsors](https://github.com/juliangruber/sponsors)!
Do you want to support modules like this to improve their quality, stability and weigh in on new features? Then please consider donating to my [Patreon](https://www.patreon.com/juliangruber). Not sure how much of my modules you're using? Try [feross/thanks](https://github.com/feross/thanks)!
## Security contact information
To report a security vulnerability, please use the
[Tidelift security contact](https://tidelift.com/security).
Tidelift will coordinate the fix and disclosure.
## License
(MIT)
Copyright (c) 2013 Julian Gruber &lt;julian@juliangruber.com&gt;
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

203
@capacitor/assets/node_modules/brace-expansion/index.js generated vendored Normal file
View file

@ -0,0 +1,203 @@
var balanced = require('balanced-match');
module.exports = expandTop;
var escSlash = '\0SLASH'+Math.random()+'\0';
var escOpen = '\0OPEN'+Math.random()+'\0';
var escClose = '\0CLOSE'+Math.random()+'\0';
var escComma = '\0COMMA'+Math.random()+'\0';
var escPeriod = '\0PERIOD'+Math.random()+'\0';
function numeric(str) {
return parseInt(str, 10) == str
? parseInt(str, 10)
: str.charCodeAt(0);
}
function escapeBraces(str) {
return str.split('\\\\').join(escSlash)
.split('\\{').join(escOpen)
.split('\\}').join(escClose)
.split('\\,').join(escComma)
.split('\\.').join(escPeriod);
}
function unescapeBraces(str) {
return str.split(escSlash).join('\\')
.split(escOpen).join('{')
.split(escClose).join('}')
.split(escComma).join(',')
.split(escPeriod).join('.');
}
// Basically just str.split(","), but handling cases
// where we have nested braced sections, which should be
// treated as individual members, like {a,{b,c},d}
function parseCommaParts(str) {
if (!str)
return [''];
var parts = [];
var m = balanced('{', '}', str);
if (!m)
return str.split(',');
var pre = m.pre;
var body = m.body;
var post = m.post;
var p = pre.split(',');
p[p.length-1] += '{' + body + '}';
var postParts = parseCommaParts(post);
if (post.length) {
p[p.length-1] += postParts.shift();
p.push.apply(p, postParts);
}
parts.push.apply(parts, p);
return parts;
}
function expandTop(str) {
if (!str)
return [];
// I don't know why Bash 4.3 does this, but it does.
// Anything starting with {} will have the first two bytes preserved
// but *only* at the top level, so {},a}b will not expand to anything,
// but a{},b}c will be expanded to [a}c,abc].
// One could argue that this is a bug in Bash, but since the goal of
// this module is to match Bash's rules, we escape a leading {}
if (str.substr(0, 2) === '{}') {
str = '\\{\\}' + str.substr(2);
}
return expand(escapeBraces(str), true).map(unescapeBraces);
}
function embrace(str) {
return '{' + str + '}';
}
function isPadded(el) {
return /^-?0\d/.test(el);
}
function lte(i, y) {
return i <= y;
}
function gte(i, y) {
return i >= y;
}
function expand(str, isTop) {
var expansions = [];
var m = balanced('{', '}', str);
if (!m) return [str];
// no need to expand pre, since it is guaranteed to be free of brace-sets
var pre = m.pre;
var post = m.post.length
? expand(m.post, false)
: [''];
if (/\$$/.test(m.pre)) {
for (var k = 0; k < post.length; k++) {
var expansion = pre+ '{' + m.body + '}' + post[k];
expansions.push(expansion);
}
} else {
var isNumericSequence = /^-?\d+\.\.-?\d+(?:\.\.-?\d+)?$/.test(m.body);
var isAlphaSequence = /^[a-zA-Z]\.\.[a-zA-Z](?:\.\.-?\d+)?$/.test(m.body);
var isSequence = isNumericSequence || isAlphaSequence;
var isOptions = m.body.indexOf(',') >= 0;
if (!isSequence && !isOptions) {
// {a},b}
if (m.post.match(/,.*\}/)) {
str = m.pre + '{' + m.body + escClose + m.post;
return expand(str);
}
return [str];
}
var n;
if (isSequence) {
n = m.body.split(/\.\./);
} else {
n = parseCommaParts(m.body);
if (n.length === 1) {
// x{{a,b}}y ==> x{a}y x{b}y
n = expand(n[0], false).map(embrace);
if (n.length === 1) {
return post.map(function(p) {
return m.pre + n[0] + p;
});
}
}
}
// at this point, n is the parts, and we know it's not a comma set
// with a single entry.
var N;
if (isSequence) {
var x = numeric(n[0]);
var y = numeric(n[1]);
var width = Math.max(n[0].length, n[1].length)
var incr = n.length == 3
? Math.abs(numeric(n[2]))
: 1;
var test = lte;
var reverse = y < x;
if (reverse) {
incr *= -1;
test = gte;
}
var pad = n.some(isPadded);
N = [];
for (var i = x; test(i, y); i += incr) {
var c;
if (isAlphaSequence) {
c = String.fromCharCode(i);
if (c === '\\')
c = '';
} else {
c = String(i);
if (pad) {
var need = width - c.length;
if (need > 0) {
var z = new Array(need + 1).join('0');
if (i < 0)
c = '-' + z + c.slice(1);
else
c = z + c;
}
}
}
N.push(c);
}
} else {
N = [];
for (var j = 0; j < n.length; j++) {
N.push.apply(N, expand(n[j], false));
}
}
for (var j = 0; j < N.length; j++) {
for (var k = 0; k < post.length; k++) {
var expansion = pre + N[j] + post[k];
if (!isTop || isSequence || expansion)
expansions.push(expansion);
}
}
}
return expansions;
}

View file

@ -0,0 +1,46 @@
{
"name": "brace-expansion",
"description": "Brace expansion as known from sh/bash",
"version": "2.0.1",
"repository": {
"type": "git",
"url": "git://github.com/juliangruber/brace-expansion.git"
},
"homepage": "https://github.com/juliangruber/brace-expansion",
"main": "index.js",
"scripts": {
"test": "tape test/*.js",
"gentest": "bash test/generate.sh",
"bench": "matcha test/perf/bench.js"
},
"dependencies": {
"balanced-match": "^1.0.0"
},
"devDependencies": {
"@c4312/matcha": "^1.3.1",
"tape": "^4.6.0"
},
"keywords": [],
"author": {
"name": "Julian Gruber",
"email": "mail@juliangruber.com",
"url": "http://juliangruber.com"
},
"license": "MIT",
"testling": {
"files": "test/*.js",
"browsers": [
"ie/8..latest",
"firefox/20..latest",
"firefox/nightly",
"chrome/25..latest",
"chrome/canary",
"opera/12..latest",
"opera/next",
"safari/5.1..latest",
"ipad/6.0..latest",
"iphone/6.0..latest",
"android-browser/4.2..latest"
]
}
}

139
@capacitor/assets/node_modules/cliui/CHANGELOG.md generated vendored Normal file
View file

@ -0,0 +1,139 @@
# Change Log
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
## [8.0.1](https://github.com/yargs/cliui/compare/v8.0.0...v8.0.1) (2022-10-01)
### Bug Fixes
* **deps:** move rollup-plugin-ts to dev deps ([#124](https://github.com/yargs/cliui/issues/124)) ([7c8bd6b](https://github.com/yargs/cliui/commit/7c8bd6ba024d61e4eeae310c7959ab8ab6829081))
## [8.0.0](https://github.com/yargs/cliui/compare/v7.0.4...v8.0.0) (2022-09-30)
### ⚠ BREAKING CHANGES
* **deps:** drop Node 10 to release CVE-2021-3807 patch (#122)
### Bug Fixes
* **deps:** drop Node 10 to release CVE-2021-3807 patch ([#122](https://github.com/yargs/cliui/issues/122)) ([f156571](https://github.com/yargs/cliui/commit/f156571ce4f2ebf313335e3a53ad905589da5a30))
### [7.0.4](https://www.github.com/yargs/cliui/compare/v7.0.3...v7.0.4) (2020-11-08)
### Bug Fixes
* **deno:** import UIOptions from definitions ([#97](https://www.github.com/yargs/cliui/issues/97)) ([f04f343](https://www.github.com/yargs/cliui/commit/f04f3439bc78114c7e90f82ff56f5acf16268ea8))
### [7.0.3](https://www.github.com/yargs/cliui/compare/v7.0.2...v7.0.3) (2020-10-16)
### Bug Fixes
* **exports:** node 13.0 and 13.1 require the dotted object form _with_ a string fallback ([#93](https://www.github.com/yargs/cliui/issues/93)) ([eca16fc](https://www.github.com/yargs/cliui/commit/eca16fc05d26255df3280906c36d7f0e5b05c6e9))
### [7.0.2](https://www.github.com/yargs/cliui/compare/v7.0.1...v7.0.2) (2020-10-14)
### Bug Fixes
* **exports:** node 13.0-13.6 require a string fallback ([#91](https://www.github.com/yargs/cliui/issues/91)) ([b529d7e](https://www.github.com/yargs/cliui/commit/b529d7e432901af1af7848b23ed6cf634497d961))
### [7.0.1](https://www.github.com/yargs/cliui/compare/v7.0.0...v7.0.1) (2020-08-16)
### Bug Fixes
* **build:** main should be build/index.cjs ([dc29a3c](https://www.github.com/yargs/cliui/commit/dc29a3cc617a410aa850e06337b5954b04f2cb4d))
## [7.0.0](https://www.github.com/yargs/cliui/compare/v6.0.0...v7.0.0) (2020-08-16)
### ⚠ BREAKING CHANGES
* tsc/ESM/Deno support (#82)
* modernize deps and build (#80)
### Build System
* modernize deps and build ([#80](https://www.github.com/yargs/cliui/issues/80)) ([339d08d](https://www.github.com/yargs/cliui/commit/339d08dc71b15a3928aeab09042af94db2f43743))
### Code Refactoring
* tsc/ESM/Deno support ([#82](https://www.github.com/yargs/cliui/issues/82)) ([4b777a5](https://www.github.com/yargs/cliui/commit/4b777a5fe01c5d8958c6708695d6aab7dbe5706c))
## [6.0.0](https://www.github.com/yargs/cliui/compare/v5.0.0...v6.0.0) (2019-11-10)
### ⚠ BREAKING CHANGES
* update deps, drop Node 6
### Code Refactoring
* update deps, drop Node 6 ([62056df](https://www.github.com/yargs/cliui/commit/62056df))
## [5.0.0](https://github.com/yargs/cliui/compare/v4.1.0...v5.0.0) (2019-04-10)
### Bug Fixes
* Update wrap-ansi to fix compatibility with latest versions of chalk. ([#60](https://github.com/yargs/cliui/issues/60)) ([7bf79ae](https://github.com/yargs/cliui/commit/7bf79ae))
### BREAKING CHANGES
* Drop support for node < 6.
<a name="4.1.0"></a>
## [4.1.0](https://github.com/yargs/cliui/compare/v4.0.0...v4.1.0) (2018-04-23)
### Features
* add resetOutput method ([#57](https://github.com/yargs/cliui/issues/57)) ([7246902](https://github.com/yargs/cliui/commit/7246902))
<a name="4.0.0"></a>
## [4.0.0](https://github.com/yargs/cliui/compare/v3.2.0...v4.0.0) (2017-12-18)
### Bug Fixes
* downgrades strip-ansi to version 3.0.1 ([#54](https://github.com/yargs/cliui/issues/54)) ([5764c46](https://github.com/yargs/cliui/commit/5764c46))
* set env variable FORCE_COLOR. ([#56](https://github.com/yargs/cliui/issues/56)) ([7350e36](https://github.com/yargs/cliui/commit/7350e36))
### Chores
* drop support for node < 4 ([#53](https://github.com/yargs/cliui/issues/53)) ([b105376](https://github.com/yargs/cliui/commit/b105376))
### Features
* add fallback for window width ([#45](https://github.com/yargs/cliui/issues/45)) ([d064922](https://github.com/yargs/cliui/commit/d064922))
### BREAKING CHANGES
* officially drop support for Node < 4
<a name="3.2.0"></a>
## [3.2.0](https://github.com/yargs/cliui/compare/v3.1.2...v3.2.0) (2016-04-11)
### Bug Fixes
* reduces tarball size ([acc6c33](https://github.com/yargs/cliui/commit/acc6c33))
### Features
* adds standard-version for release management ([ff84e32](https://github.com/yargs/cliui/commit/ff84e32))

14
@capacitor/assets/node_modules/cliui/LICENSE.txt generated vendored Normal file
View file

@ -0,0 +1,14 @@
Copyright (c) 2015, Contributors
Permission to use, copy, modify, and/or distribute this software
for any purpose with or without fee is hereby granted, provided
that the above copyright notice and this permission notice
appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE
LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES
OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

141
@capacitor/assets/node_modules/cliui/README.md generated vendored Normal file
View file

@ -0,0 +1,141 @@
# cliui
![ci](https://github.com/yargs/cliui/workflows/ci/badge.svg)
[![NPM version](https://img.shields.io/npm/v/cliui.svg)](https://www.npmjs.com/package/cliui)
[![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-yellow.svg)](https://conventionalcommits.org)
![nycrc config on GitHub](https://img.shields.io/nycrc/yargs/cliui)
easily create complex multi-column command-line-interfaces.
## Example
```js
const ui = require('cliui')()
ui.div('Usage: $0 [command] [options]')
ui.div({
text: 'Options:',
padding: [2, 0, 1, 0]
})
ui.div(
{
text: "-f, --file",
width: 20,
padding: [0, 4, 0, 4]
},
{
text: "the file to load." +
chalk.green("(if this description is long it wraps).")
,
width: 20
},
{
text: chalk.red("[required]"),
align: 'right'
}
)
console.log(ui.toString())
```
## Deno/ESM Support
As of `v7` `cliui` supports [Deno](https://github.com/denoland/deno) and
[ESM](https://nodejs.org/api/esm.html#esm_ecmascript_modules):
```typescript
import cliui from "https://deno.land/x/cliui/deno.ts";
const ui = cliui({})
ui.div('Usage: $0 [command] [options]')
ui.div({
text: 'Options:',
padding: [2, 0, 1, 0]
})
ui.div({
text: "-f, --file",
width: 20,
padding: [0, 4, 0, 4]
})
console.log(ui.toString())
```
<img width="500" src="screenshot.png">
## Layout DSL
cliui exposes a simple layout DSL:
If you create a single `ui.div`, passing a string rather than an
object:
* `\n`: characters will be interpreted as new rows.
* `\t`: characters will be interpreted as new columns.
* `\s`: characters will be interpreted as padding.
**as an example...**
```js
var ui = require('./')({
width: 60
})
ui.div(
'Usage: node ./bin/foo.js\n' +
' <regex>\t provide a regex\n' +
' <glob>\t provide a glob\t [required]'
)
console.log(ui.toString())
```
**will output:**
```shell
Usage: node ./bin/foo.js
<regex> provide a regex
<glob> provide a glob [required]
```
## Methods
```js
cliui = require('cliui')
```
### cliui({width: integer})
Specify the maximum width of the UI being generated.
If no width is provided, cliui will try to get the current window's width and use it, and if that doesn't work, width will be set to `80`.
### cliui({wrap: boolean})
Enable or disable the wrapping of text in a column.
### cliui.div(column, column, column)
Create a row with any number of columns, a column
can either be a string, or an object with the following
options:
* **text:** some text to place in the column.
* **width:** the width of a column.
* **align:** alignment, `right` or `center`.
* **padding:** `[top, right, bottom, left]`.
* **border:** should a border be placed around the div?
### cliui.span(column, column, column)
Similar to `div`, except the next row will be appended without
a new line being created.
### cliui.resetOutput()
Resets the UI elements of the current cliui instance, maintaining the values
set for `width` and `wrap`.

Some files were not shown because too many files have changed in this diff Show more