Skip to content

Commit

Permalink
feat(cli): add capacitor run command (#3599)
Browse files Browse the repository at this point in the history
  • Loading branch information
imhoffd committed Oct 16, 2020
1 parent 275710f commit 68e872d
Show file tree
Hide file tree
Showing 25 changed files with 644 additions and 331 deletions.
12 changes: 7 additions & 5 deletions cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,19 +42,22 @@
},
"license": "MIT",
"dependencies": {
"@ionic/cli-framework-output": "^2.2.0",
"@ionic/utils-terminal": "^2.2.1",
"@ionic/cli-framework-output": "^2.2.1",
"@ionic/utils-fs": "^3.1.5",
"@ionic/utils-subprocess": "^2.1.6",
"@ionic/utils-terminal": "^2.3.0",
"commander": "^6.0.0",
"fs-extra": "^9.0.1",
"debug": "^4.2.0",
"kleur": "^4.1.1",
"native-run": "^1.2.1",
"open": "^7.1.0",
"plist": "^3.0.1",
"prompts": "^2.3.2",
"semver": "^7.3.2",
"which": "^2.0.2",
"xml2js": "^0.4.19"
},
"devDependencies": {
"@types/debug": "^4.1.5",
"@types/fs-extra": "^9.0.1",
"@types/jest": "^26.0.4",
"@types/open": "^6.1.0",
Expand All @@ -63,7 +66,6 @@
"@types/semver": "^7.3.1",
"@types/slice-ansi": "^4.0.0",
"@types/tmp": "^0.2.0",
"@types/which": "^1.0.28",
"@types/xml2js": "^0.4.2",
"jest": "^26.1.0",
"rimraf": "^3.0.2",
Expand Down
14 changes: 6 additions & 8 deletions cli/src/android/add.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { pathExists, writeFile } from '@ionic/utils-fs';
import { homedir } from 'os';
import { join } from 'path';

import c from '../colors';
import { copyTemplate, runCommand, runTask } from '../common';
import type { Config } from '../definitions';
import { existsAsync, writeFileAsync } from '../util/fs';

export async function addAndroid(config: Config): Promise<void> {
await runTask(
Expand All @@ -24,7 +24,7 @@ export async function addAndroid(config: Config): Promise<void> {

async function createLocalProperties(platformDir: string) {
const defaultAndroidPath = join(homedir(), 'Library/Android/sdk');
if (await existsAsync(defaultAndroidPath)) {
if (await pathExists(defaultAndroidPath)) {
const localSettings = `
## This file is automatically generated by Android Studio.
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
Expand All @@ -37,11 +37,9 @@ async function createLocalProperties(platformDir: string) {
# header note.
sdk.dir=${defaultAndroidPath}
`;
await writeFileAsync(
join(platformDir, 'local.properties'),
localSettings,
'utf8',
);
await writeFile(join(platformDir, 'local.properties'), localSettings, {
encoding: 'utf-8',
});

// Only sync if we were able to create the local properties above, otherwise
// this will fail
Expand All @@ -57,5 +55,5 @@ sdk.dir=${defaultAndroidPath}
}

async function gradleSync(platformDir: string) {
await runCommand(`${platformDir}/gradlew`);
await runCommand(`${platformDir}/gradlew`, []);
}
59 changes: 31 additions & 28 deletions cli/src/android/common.ts
Original file line number Diff line number Diff line change
@@ -1,41 +1,44 @@
import { mkdirs } from 'fs-extra';
import {
copy,
remove,
mkdirp,
readFile,
pathExists,
writeFile,
} from '@ionic/utils-fs';
import { join, resolve } from 'path';

import { checkCapacitorPlatform } from '../common';
import { getIncompatibleCordovaPlugins } from '../cordova';
import type { Config } from '../definitions';
import type { Plugin } from '../plugin';
import { PluginType, getPluginPlatform } from '../plugin';
import {
convertToUnixPath,
copyAsync,
existsAsync,
existsSync,
readFileAsync,
removeAsync,
writeFileAsync,
} from '../util/fs';
import { convertToUnixPath } from '../util/fs';

export async function checkAndroidPackage(
config: Config,
): Promise<string | null> {
return checkCapacitorPlatform(config, 'android');
}

export function getAndroidPlugins(allPlugins: Plugin[]): Plugin[] {
const resolved = allPlugins.map(plugin => resolvePlugin(plugin));
return resolved.filter(plugin => !!plugin) as Plugin[];
export async function getAndroidPlugins(
allPlugins: Plugin[],
): Promise<Plugin[]> {
const resolved = await Promise.all(
allPlugins.map(async plugin => await resolvePlugin(plugin)),
);
return resolved.filter((plugin): plugin is Plugin => !!plugin);
}

export function resolvePlugin(plugin: Plugin): Plugin | null {
export async function resolvePlugin(plugin: Plugin): Promise<Plugin | null> {
const platform = 'android';
if (plugin.manifest?.android) {
let pluginFilesPath = plugin.manifest.android.src
? plugin.manifest.android.src
: platform;
const absolutePath = join(plugin.rootPath, pluginFilesPath, plugin.id);
// Android folder shouldn't have subfolders, but they used to, so search for them for compatibility reasons
if (existsSync(absolutePath)) {
if (await pathExists(absolutePath)) {
pluginFilesPath = join(platform, plugin.id);
}
plugin.android = {
Expand Down Expand Up @@ -79,13 +82,13 @@ export async function editProjectSettingsAndroid(
'app/build.gradle',
);

let manifestContent = await readFileAsync(manifestPath, 'utf8');
let manifestContent = await readFile(manifestPath, { encoding: 'utf-8' });

manifestContent = manifestContent.replace(
/com.getcapacitor.myapp/g,
`${appId}`,
);
await writeFileAsync(manifestPath, manifestContent, 'utf8');
await writeFile(manifestPath, manifestContent, { encoding: 'utf-8' });

const domainPath = appId.split('.').join('/');
// Make the package source path to the new plugin Java file
Expand All @@ -94,11 +97,11 @@ export async function editProjectSettingsAndroid(
`app/src/main/java/${domainPath}`,
);

if (!(await existsAsync(newJavaPath))) {
await mkdirs(newJavaPath);
if (!(await pathExists(newJavaPath))) {
await mkdirp(newJavaPath);
}

await copyAsync(
await copy(
resolve(
config.android.platformDirAbs,
'app/src/main/java/com/getcapacitor/myapp/MainActivity.java',
Expand All @@ -107,7 +110,7 @@ export async function editProjectSettingsAndroid(
);

if (appId.split('.')[1] !== 'getcapacitor') {
await removeAsync(
await remove(
resolve(
config.android.platformDirAbs,
'app/src/main/java/com/getcapacitor',
Expand All @@ -117,7 +120,7 @@ export async function editProjectSettingsAndroid(

// Remove our template 'com' folder if their ID doesn't have it
if (appId.split('.')[0] !== 'com') {
await removeAsync(
await remove(
resolve(config.android.platformDirAbs, 'app/src/main/java/com/'),
);
}
Expand All @@ -128,34 +131,34 @@ export async function editProjectSettingsAndroid(
newJavaPath,
'MainActivity.java',
);
let activityContent = await readFileAsync(activityPath, 'utf8');
let activityContent = await readFile(activityPath, { encoding: 'utf-8' });

activityContent = activityContent.replace(
/package ([^;]*)/,
`package ${appId}`,
);
await writeFileAsync(activityPath, activityContent, 'utf8');
await writeFile(activityPath, activityContent, { encoding: 'utf-8' });

// Update the applicationId in build.gradle
let gradleContent = await readFileAsync(buildGradlePath, 'utf8');
let gradleContent = await readFile(buildGradlePath, { encoding: 'utf-8' });
gradleContent = gradleContent.replace(
/applicationId "[^"]+"/,
`applicationId "${appId}"`,
);

await writeFileAsync(buildGradlePath, gradleContent, 'utf8');
await writeFile(buildGradlePath, gradleContent, { encoding: 'utf-8' });

// Update the settings in res/values/strings.xml
const stringsPath = resolve(
config.android.platformDirAbs,
'app/src/main/res/values/strings.xml',
);
let stringsContent = await readFileAsync(stringsPath, 'utf8');
let stringsContent = await readFile(stringsPath, { encoding: 'utf-8' });
stringsContent = stringsContent.replace(/com.getcapacitor.myapp/g, appId);
stringsContent = stringsContent.replace(
/My App/g,
appName.replace(/'/g, `\\'`),
);

await writeFileAsync(stringsPath, stringsContent);
await writeFile(stringsPath, stringsContent);
}
26 changes: 13 additions & 13 deletions cli/src/android/doctor.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { pathExists, readFile } from '@ionic/utils-fs';
import { accessSync } from 'fs';
import { join } from 'path';

import c from '../colors';
import { check, logFatal, logSuccess, readXML } from '../common';
import type { Config } from '../definitions';
import { existsAsync, readFileAsync } from '../util/fs';

export async function doctorAndroid(config: Config): Promise<void> {
try {
Expand All @@ -21,29 +21,29 @@ export async function doctorAndroid(config: Config): Promise<void> {

async function checkAppSrcDirs(config: Config) {
const appDir = join(config.android.platformDirAbs, 'app');
if (!(await existsAsync(appDir))) {
if (!(await pathExists(appDir))) {
return `${c.strong('app')} directory is missing in ${
config.android.platformDir
}`;
}

const appSrcDir = join(appDir, 'src');
if (!(await existsAsync(appSrcDir))) {
if (!(await pathExists(appSrcDir))) {
return `${c.strong('src')} directory is missing in ${appDir}`;
}

const appSrcMainDir = join(appSrcDir, 'main');
if (!(await existsAsync(appSrcMainDir))) {
if (!(await pathExists(appSrcMainDir))) {
return `${c.strong('main')} directory is missing in ${appSrcDir}`;
}

const appSrcMainAssetsDir = join(appSrcMainDir, 'assets');
if (!(await existsAsync(appSrcMainAssetsDir))) {
if (!(await pathExists(appSrcMainAssetsDir))) {
return `${c.strong('assets')} directory is missing in ${appSrcMainDir}`;
}

const appSrcMainAssetsWwwDir = join(appSrcMainAssetsDir, 'public');
if (!(await existsAsync(appSrcMainAssetsWwwDir))) {
if (!(await pathExists(appSrcMainAssetsWwwDir))) {
return `${c.strong(
'public',
)} directory is missing in ${appSrcMainAssetsDir}`;
Expand All @@ -53,7 +53,7 @@ async function checkAppSrcDirs(config: Config) {
appSrcMainAssetsWwwDir,
'index.html',
);
if (!(await existsAsync(appSrcMainAssetsWwwIndexHtmlDir))) {
if (!(await pathExists(appSrcMainAssetsWwwIndexHtmlDir))) {
return `${c.strong(
'index.html',
)} file is missing in ${appSrcMainAssetsWwwDir}`;
Expand All @@ -66,7 +66,7 @@ async function checkAndroidManifestFile(config: Config, appSrcMainDir: string) {
const manifestFileName = 'AndroidManifest.xml';
const manifestFilePath = join(appSrcMainDir, manifestFileName);

if (!(await existsAsync(manifestFilePath))) {
if (!(await pathExists(manifestFilePath))) {
return `${c.strong(manifestFileName)} is missing in ${appSrcMainDir}`;
}

Expand Down Expand Up @@ -183,7 +183,7 @@ async function checkPackage(
}

const appSrcMainJavaDir = join(appSrcMainDir, 'java');
if (!(await existsAsync(appSrcMainJavaDir))) {
if (!(await pathExists(appSrcMainJavaDir))) {
return `${c.strong('java')} directory is missing in ${appSrcMainDir}`;
}

Expand All @@ -208,7 +208,7 @@ async function checkPackage(
const mainActivityClassFileName = `${mainActivityClassName}.java`;
const mainActivityClassFilePath = join(checkPath, mainActivityClassFileName);

if (!(await existsAsync(mainActivityClassFilePath))) {
if (!(await pathExists(mainActivityClassFilePath))) {
return `Main activity file (${mainActivityClassFileName}) is missing in ${checkPath}`;
}

Expand All @@ -220,11 +220,11 @@ async function checkBuildGradle(config: Config, packageId: string) {
const fileName = 'build.gradle';
const filePath = join(appDir, fileName);

if (!(await existsAsync(filePath))) {
if (!(await pathExists(filePath))) {
return `${fileName} file is missing in ${appDir}`;
}

let fileContent = await readFileAsync(filePath, 'utf8');
let fileContent = await readFile(filePath, { encoding: 'utf-8' });

fileContent = fileContent.replace(/'|"/g, '').replace(/\s+/g, ' ');

Expand All @@ -243,7 +243,7 @@ async function checkGradlew(config: Config) {
const fileName = 'gradlew';
const filePath = join(config.android.platformDirAbs, fileName);

if (!(await existsAsync(filePath))) {
if (!(await pathExists(filePath))) {
return `${c.strong(fileName)} file is missing in ${
config.android.platformDir
}`;
Expand Down
13 changes: 8 additions & 5 deletions cli/src/android/open.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { pathExists } from '@ionic/utils-fs';
import open from 'open';

import { runCommand } from '../common';
import type { Config } from '../definitions';
import { OS } from '../definitions';
import { logger } from '../log';
import { existsSync } from '../util/fs';

export async function openAndroid(config: Config): Promise<void> {
logger.info(`Opening Android project at ${config.android.platformDir}.`);
Expand All @@ -19,10 +19,13 @@ export async function openAndroid(config: Config): Promise<void> {
case OS.Windows: {
let androidStudioPath = config.windows.androidStudioPath;
try {
if (!existsSync(androidStudioPath)) {
let commandResult = await runCommand(
'REG QUERY "HKEY_LOCAL_MACHINE\\SOFTWARE\\Android Studio" /v Path',
);
if (!(await pathExists(androidStudioPath))) {
let commandResult = await runCommand('REG', [
'QUERY',
'HKEY_LOCAL_MACHINE\\SOFTWARE\\Android Studio',
'/v',
'Path',
]);
commandResult = commandResult.replace(/(\r\n|\n|\r)/gm, '');
const ix = commandResult.indexOf('REG_SZ');
if (ix > 0) {
Expand Down
Loading

0 comments on commit 68e872d

Please sign in to comment.