Skip to content
This repository has been archived by the owner on Jan 18, 2024. It is now read-only.

Commit

Permalink
Added custom path options (#1905)
Browse files Browse the repository at this point in the history
  • Loading branch information
EvanBacon authored Apr 18, 2020
1 parent 7a3282e commit 4d2dd74
Show file tree
Hide file tree
Showing 5 changed files with 157 additions and 66 deletions.
77 changes: 46 additions & 31 deletions packages/uri-scheme/src/Android.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,27 @@ import * as Scheme from '@expo/config/build/android/Scheme';
import spawnAsync from '@expo/spawn-async';
import chalk from 'chalk';
import { sync } from 'glob';
import path from 'path';
import { join } from 'path';

import { CommandError, Options } from './Options';

const CANT_START_ACTIVITY_ERROR = 'Activity not started, unable to resolve Intent';
const BEGINNING_OF_ADB_ERROR_MESSAGE = 'error: ';

export function isAvailable(projectRoot: string): boolean {
const reactNativeAndroid = sync(
path.join(projectRoot, 'android/app/src/main/AndroidManifest.xml')
);
const currentAndroid = sync(path.join(projectRoot, 'app/src/main/AndroidManifest.xml'));
const reactNativeAndroid = sync(join(projectRoot, 'android/app/src/main/AndroidManifest.xml'));
const currentAndroid = sync(join(projectRoot, 'app/src/main/AndroidManifest.xml'));
return !!currentAndroid.length || !!reactNativeAndroid.length;
}

export async function addAsync({ dryRun, uri, projectRoot }: Options): Promise<boolean> {
const manifestPath = getConfigPath(projectRoot);
let manifest = await readConfigAsync(manifestPath);
export async function addAsync({
dryRun,
uri,
manifestPath,
projectRoot,
}: Pick<Options, 'dryRun' | 'uri' | 'manifestPath' | 'projectRoot'>): Promise<boolean> {
const resolvedManifestPath = manifestPath ?? getConfigPath(projectRoot);
let manifest = await readConfigAsync(resolvedManifestPath);

if (!Scheme.ensureManifestHasValidIntentFilter(manifest)) {
throw new CommandError(
Expand All @@ -36,7 +39,7 @@ export async function addAsync({ dryRun, uri, projectRoot }: Options): Promise<b
if (Scheme.hasScheme(uri, manifest)) {
console.log(
chalk.yellow(
`\u203A Android: URI scheme "${uri}" already exists in AndroidManifest.xml at: ${manifestPath}`
`\u203A Android: URI scheme "${uri}" already exists in AndroidManifest.xml at: ${resolvedManifestPath}`
)
);
return false;
Expand All @@ -45,17 +48,22 @@ export async function addAsync({ dryRun, uri, projectRoot }: Options): Promise<b
manifest = Scheme.appendScheme(uri, manifest);

if (dryRun) {
console.log(chalk.magenta('Write manifest to: ', manifestPath));
console.log(chalk.magenta('Write manifest to: ', resolvedManifestPath));
console.log(format(manifest));
return false;
}
await writeConfigAsync(manifestPath, manifest);
await writeConfigAsync(resolvedManifestPath, manifest);
return true;
}

export async function removeAsync({ dryRun, uri, projectRoot }: Options): Promise<boolean> {
const manifestPath = getConfigPath(projectRoot);
let manifest = await readConfigAsync(manifestPath);
export async function removeAsync({
dryRun,
uri,
manifestPath,
projectRoot,
}: Pick<Options, 'dryRun' | 'uri' | 'manifestPath' | 'projectRoot'>): Promise<boolean> {
const resolvedManifestPath = manifestPath ?? getConfigPath(projectRoot);
let manifest = await readConfigAsync(resolvedManifestPath);

if (!Scheme.ensureManifestHasValidIntentFilter(manifest)) {
throw new CommandError(
Expand All @@ -67,7 +75,7 @@ export async function removeAsync({ dryRun, uri, projectRoot }: Options): Promis
if (!Scheme.hasScheme(uri, manifest)) {
console.log(
chalk.yellow(
`\u203A Android: URI scheme "${uri}" does not exist in AndroidManifest.xml at: ${manifestPath}`
`\u203A Android: URI scheme "${uri}" does not exist in AndroidManifest.xml at: ${resolvedManifestPath}`
)
);
return false;
Expand All @@ -76,11 +84,11 @@ export async function removeAsync({ dryRun, uri, projectRoot }: Options): Promis
manifest = Scheme.removeScheme(uri, manifest);

if (dryRun) {
console.log(chalk.magenta('Write manifest to: ', manifestPath));
console.log(chalk.magenta('Write manifest to: ', resolvedManifestPath));
console.log(format(manifest));
return false;
}
await writeConfigAsync(manifestPath, manifest);
await writeConfigAsync(resolvedManifestPath, manifest);
return true;
}

Expand Down Expand Up @@ -124,9 +132,12 @@ async function openUrlAsync(...props: (string | null)[]): Promise<string> {
return output;
}

export async function openAsync({ projectRoot, uri }: Options): Promise<string> {
const manifestPath = getConfigPath(projectRoot);
const manifest = await readConfigAsync(manifestPath);
export async function openAsync({
projectRoot,
manifestPath,
uri,
}: Pick<Options, 'projectRoot' | 'manifestPath' | 'uri'>): Promise<string> {
const manifest = await readConfigAsync(manifestPath ?? getConfigPath(projectRoot));

let androidPackage: string | null = null;
try {
Expand All @@ -135,35 +146,39 @@ export async function openAsync({ projectRoot, uri }: Options): Promise<string>
return await openUrlAsync(uri, androidPackage);
}

export async function getAsync({ projectRoot }: Options): Promise<string[]> {
const manifestPath = getConfigPath(projectRoot);
const manifest = await readConfigAsync(manifestPath);
export async function getAsync({
projectRoot,
manifestPath,
}: Pick<Options, 'projectRoot' | 'manifestPath'>): Promise<string[]> {
const manifest = await readConfigAsync(manifestPath ?? getConfigPath(projectRoot));
return await Scheme.getSchemesFromManifest(manifest);
}

export async function getProjectIdAsync({ projectRoot }: Options): Promise<string> {
const manifestPath = getConfigPath(projectRoot);
const manifest = await readConfigAsync(manifestPath);
export async function getProjectIdAsync({
projectRoot,
manifestPath,
}: Pick<Options, 'projectRoot' | 'manifestPath'>): Promise<string> {
const resolvedManifestPath = manifestPath ?? getConfigPath(projectRoot);
const manifest = await readConfigAsync(resolvedManifestPath);
const androidPackage = await getPackageAsync(manifest);
if (!androidPackage)
throw new CommandError(
`Android: Failed to resolve android package for Manifest at path: ${manifestPath}`
`Android: Failed to resolve android package for Manifest at path: ${resolvedManifestPath}`
);
return androidPackage;
}

export function getConfigPath(projectRoot: string): string {
const rnManifestPaths = sync(path.join(projectRoot, 'android/app/src/main/AndroidManifest.xml'));
const rnManifestPaths = sync(join(projectRoot, 'android/app/src/main/AndroidManifest.xml'));
if (rnManifestPaths.length) {
return rnManifestPaths[0];
}
const manifestPaths = sync(path.join(projectRoot, 'app/src/main/AndroidManifest.xml'));
const manifestPaths = sync(join(projectRoot, 'app/src/main/AndroidManifest.xml'));
return manifestPaths[0];
}

async function readConfigAsync(path: string): Promise<any> {
let androidManifestJSON = await readAndroidManifestAsync(path);
return androidManifestJSON;
return await readAndroidManifestAsync(path);
}

async function writeConfigAsync(path: string, result: any) {
Expand Down
52 changes: 43 additions & 9 deletions packages/uri-scheme/src/CLI.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
#!/usr/bin/env node
import chalk from 'chalk';
import { Command } from 'commander';
import { resolve } from 'path';

import * as Android from './Android';
import * as Ios from './Ios';
import { CommandError, Options } from './Options';
import shouldUpdate from './update';
import * as URIScheme from './URIScheme';
Expand Down Expand Up @@ -31,6 +34,11 @@ buildCommand('add', ['com.app', 'myapp'])
.description('Add URI schemes to a native app')
.option('-n, --name <string>', 'Name to use on iOS.')
.option('-r, --role <string>', 'Role to use on iOS: Editor, Viewer')
.option(
'--manifest-path <string>',
"Custom path to use for an Android project's AndroidManifest.xml"
)
.option('--info-path <string>', "Custom path to use for an iOS project's Info.plist")
.option('--dry-run', 'View the proposed change')
.action(async (uri: string, args: any) => {
try {
Expand All @@ -44,6 +52,11 @@ buildCommand('add', ['com.app', 'myapp'])

buildCommand('remove', ['com.app', 'myapp'])
.description('Remove URI schemes from a native app')
.option(
'--manifest-path <string>',
"Custom path to use for an Android project's AndroidManifest.xml"
)
.option('--info-path <string>', "Custom path to use for an iOS project's Info.plist")
.option('--dry-run', 'View the proposed change')
.action(async (uri: string, args: any) => {
try {
Expand All @@ -57,6 +70,10 @@ buildCommand('remove', ['com.app', 'myapp'])

buildCommand('open', ['com.app://oauth', 'http://expo.io'])
.description('Open a URI scheme in a running simulator or emulator')
.option(
'--manifest-path <string>',
"Custom path to use for an Android project's AndroidManifest.xml"
)
.action(async (uri: string, args: any) => {
try {
const options = await parseArgsAsync(uri, args);
Expand All @@ -69,6 +86,11 @@ buildCommand('open', ['com.app://oauth', 'http://expo.io'])

buildCommand('list')
.description('List the existing URI scheme prefixes for a native app')
.option(
'--manifest-path <string>',
"Custom path to use for an Android project's AndroidManifest.xml"
)
.option('--info-path <string>', "Custom path to use for an iOS project's Info.plist")
.action(async (uri: string, args: any) => {
try {
const options = await parseArgsAsync(uri, args);
Expand All @@ -80,8 +102,12 @@ buildCommand('list')
});

async function parseArgsAsync(uri: string, options: Options): Promise<Options> {
const projectRoot = process.cwd();
let platforms = URIScheme.getAvailablePlatforms(projectRoot);
const projectRoot = resolve(process.cwd());
options.projectRoot = projectRoot;
if (options.manifestPath) options.manifestPath = resolve(options.manifestPath);
if (options.infoPath) options.infoPath = resolve(options.infoPath);

const platforms = URIScheme.getAvailablePlatforms(options);

if (!options.android && !options.ios) {
for (const key of platforms) {
Expand All @@ -91,21 +117,29 @@ async function parseArgsAsync(uri: string, options: Options): Promise<Options> {
} else {
if (options.android) {
if (!platforms.includes('android')) {
throw new CommandError('Android not supported');
throw new CommandError(
`Android project not found in directory "${projectRoot}".\nYou can manually select an AndroidManifest.xml with \`--manifest-path\``
);
}
}
if (options.ios) {
if (!platforms.includes('ios')) {
throw new CommandError('iOS not supported');
throw new CommandError(
`iOS project not found in directory "${projectRoot}".\nYou can manually select an Info.plist with \`--info-path\``
);
}
}
}

return {
...options,
uri,
projectRoot,
};
if (options.android && !options.manifestPath) {
options.manifestPath = Android.getConfigPath(projectRoot);
}
if (options.ios && !options.infoPath) {
options.infoPath = Ios.getConfigPath(projectRoot);
}

options.uri = uri;
return options;
}

export function run() {
Expand Down
38 changes: 26 additions & 12 deletions packages/uri-scheme/src/Ios.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,23 @@ import spawnAsync from '@expo/spawn-async';
import chalk from 'chalk';
import fs from 'fs';
import { sync } from 'glob';
import path from 'path';
import { join } from 'path';

import { Options } from './Options';

export function isAvailable(projectRoot: string): boolean {
const reactNativeIos = sync(path.join(projectRoot, 'ios', '*.xcodeproj'));
const currentIos = sync(path.join(projectRoot, '*.xcodeproj'));
const reactNativeIos = sync(join(projectRoot, 'ios', '*.xcodeproj'));
const currentIos = sync(join(projectRoot, '*.xcodeproj'));
return !!currentIos.length || !!reactNativeIos.length;
}

export async function addAsync({ uri, projectRoot, dryRun }: Options): Promise<boolean> {
const infoPlistPath = getConfigPath(projectRoot);
export async function addAsync({
uri,
infoPath,
projectRoot,
dryRun,
}: Pick<Options, 'uri' | 'infoPath' | 'projectRoot' | 'dryRun'>): Promise<boolean> {
const infoPlistPath = infoPath ?? getConfigPath(projectRoot);
let config = readConfig(infoPlistPath);
Expand All @@ -40,8 +45,13 @@ export async function addAsync({ uri, projectRoot, dryRun }: Options): Promise<b
return true;
}
export async function removeAsync({ uri, projectRoot, dryRun }: Options): Promise<boolean> {
const infoPlistPath = getConfigPath(projectRoot);
export async function removeAsync({
uri,
infoPath,
projectRoot,
dryRun,
}: Pick<Options, 'uri' | 'infoPath' | 'projectRoot' | 'dryRun'>): Promise<boolean> {
const infoPlistPath = infoPath ?? getConfigPath(projectRoot);

let config = readConfig(infoPlistPath);

Expand All @@ -65,14 +75,17 @@ export async function removeAsync({ uri, projectRoot, dryRun }: Options): Promis
return true;
}

export async function openAsync({ uri }: Options): Promise<void> {
export async function openAsync({ uri }: Pick<Options, 'uri'>): Promise<void> {
await spawnAsync('xcrun', ['simctl', 'openurl', 'booted', uri], {
stdio: 'inherit',
});
}

export async function getAsync({ projectRoot }: Options): Promise<string[]> {
const infoPlistPath = getConfigPath(projectRoot);
export async function getAsync({
projectRoot,
infoPath,
}: Pick<Options, 'projectRoot' | 'infoPath'>): Promise<string[]> {
const infoPlistPath = infoPath ?? getConfigPath(projectRoot);
const rawPlist = fs.readFileSync(infoPlistPath, 'utf8');
const plistObject = plist.parse(rawPlist) as PlistObject;
const schemes = Scheme.getSchemesFromPlist(plistObject);
Expand All @@ -81,11 +94,12 @@ export async function getAsync({ projectRoot }: Options): Promise<string[]> {

export function getConfigPath(projectRoot: string): string {
// TODO: Figure out how to avoid using the Tests info.plist
const rnInfoPlistPaths = sync(path.join(projectRoot, 'ios', '*', 'Info.plist'));

const rnInfoPlistPaths = sync(join(projectRoot, 'ios', '*', 'Info.plist'));
if (rnInfoPlistPaths.length) {
return rnInfoPlistPaths[0];
}
const infoPlistPaths = sync(path.join(projectRoot, '*', 'Info.plist'));
const infoPlistPaths = sync(join(projectRoot, '*', 'Info.plist'));
return infoPlistPaths[0];
}

Expand Down
8 changes: 8 additions & 0 deletions packages/uri-scheme/src/Options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@ export type Options = {
dryRun?: boolean;
name?: string;
role?: string;
/**
* Custom path to an AndroidManifest.xml
*/
manifestPath?: string;
/**
* Custom path to an Info.plist
*/
infoPath?: string;
};

export class CommandError extends Error {
Expand Down
Loading

0 comments on commit 4d2dd74

Please sign in to comment.