From f15317f44f33956a685c265c4796c2dc8ffdeba5 Mon Sep 17 00:00:00 2001 From: Cedric van Putten Date: Mon, 4 Dec 2023 18:16:18 +0100 Subject: [PATCH] refactor(uri-scheme): move to `expo/expo` --- .github/workflows/test.yml | 1 - .github/workflows/test_windows.yml | 1 - packages/uri-scheme/LICENSE | 22 -- packages/uri-scheme/README.md | 156 +------------ packages/uri-scheme/cli.js | 5 - packages/uri-scheme/jest.config.js | 7 - packages/uri-scheme/package.json | 47 ---- packages/uri-scheme/src/Android.ts | 188 ---------------- packages/uri-scheme/src/CLI.ts | 195 ----------------- packages/uri-scheme/src/Ios.ts | 139 ------------ packages/uri-scheme/src/Options.ts | 25 --- packages/uri-scheme/src/URIScheme.ts | 206 ------------------ .../src/__tests__/URIScheme-test.ts | 39 ---- .../android/app/src/main/AndroidManifest.xml | 38 ---- .../fixtures/react-native/ios/Info.plist | 15 -- .../fixtures/react-native/ios/demo.xcodeproj | 0 packages/uri-scheme/src/index.ts | 6 - packages/uri-scheme/src/update.ts | 21 -- packages/uri-scheme/tsconfig.json | 11 - 19 files changed, 2 insertions(+), 1120 deletions(-) delete mode 100644 packages/uri-scheme/LICENSE delete mode 100755 packages/uri-scheme/cli.js delete mode 100644 packages/uri-scheme/jest.config.js delete mode 100644 packages/uri-scheme/package.json delete mode 100644 packages/uri-scheme/src/Android.ts delete mode 100644 packages/uri-scheme/src/CLI.ts delete mode 100644 packages/uri-scheme/src/Ios.ts delete mode 100644 packages/uri-scheme/src/Options.ts delete mode 100644 packages/uri-scheme/src/URIScheme.ts delete mode 100644 packages/uri-scheme/src/__tests__/URIScheme-test.ts delete mode 100644 packages/uri-scheme/src/__tests__/fixtures/react-native/android/app/src/main/AndroidManifest.xml delete mode 100644 packages/uri-scheme/src/__tests__/fixtures/react-native/ios/Info.plist delete mode 100644 packages/uri-scheme/src/__tests__/fixtures/react-native/ios/demo.xcodeproj delete mode 100644 packages/uri-scheme/src/index.ts delete mode 100644 packages/uri-scheme/src/update.ts delete mode 100644 packages/uri-scheme/tsconfig.json diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c6181362a8..5564a6e4bb 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -44,7 +44,6 @@ jobs: plist, pwa, schemer, - uri-scheme, # webpack-config, xdl, ] diff --git a/.github/workflows/test_windows.yml b/.github/workflows/test_windows.yml index 6ed1eb1dc2..fddadbad9a 100644 --- a/.github/workflows/test_windows.yml +++ b/.github/workflows/test_windows.yml @@ -43,7 +43,6 @@ jobs: plist, pwa, schemer, - uri-scheme, webpack-config, xdl, ] diff --git a/packages/uri-scheme/LICENSE b/packages/uri-scheme/LICENSE deleted file mode 100644 index 2503170729..0000000000 --- a/packages/uri-scheme/LICENSE +++ /dev/null @@ -1,22 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2019-present 650 Industries, Inc. (aka Expo) - -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. - diff --git a/packages/uri-scheme/README.md b/packages/uri-scheme/README.md index c4ff948aab..b0587b091d 100644 --- a/packages/uri-scheme/README.md +++ b/packages/uri-scheme/README.md @@ -1,155 +1,3 @@ - -

-👋 Welcome to
uri-scheme -

+# `uri-scheme` - - -

- Interact with native URI schemes -
-
- - Circle CI - -

- ---- - - - -This package provides a simple interface for modifying, viewing, and testing a project's native URI schemes. - -👋 **Notice:** This package is not limited to Expo projects! You can use it with any iOS, or Android project. - -## 🤔 Why? - -We created `uri-scheme` to make it easier to setup, test, and modify deep links, and authentication in native apps. - -## 🚀 Usage - -### CLI - -```sh -# Usage -npx uri-scheme [options] [command] - -# View all URIs for a project -npx uri-scheme list - -# Open a URI in a simulator -npx uri-scheme open --ios - -# Add a URI to your project -npx uri-scheme add -``` - -### Node - -In order to make this package fast with npx we don't ship types or doc-blocks. - -```js -import { Android, Ios } from 'uri-scheme'; - -Ios.openAsync({ uri: 'http://expo.dev/' }); -``` - -## ⚙️ Options - -For more information run `npx uri-scheme --help` (or `-h`) - -| Options | Description | -| ------------- | ------------------------- | -| -V, --version | output the version number | -| -h, --help | output usage information | - -### add - -Add URI schemes to a native app. - -**Options** - -| Options | Description | -| -------------------------- | --------------------------------------------------------------- | -| `-a, --android` | Apply action to Android | -| `-i, --ios` | Apply action to iOS | -| `-n, --name ` | Name to use on iOS. | -| `-r, --role ` | Role to use on iOS: Editor, Viewer | -| `--manifest-path ` | Custom path to use for an Android project's AndroidManifest.xml | -| `--info-path ` | Custom path to use for an iOS project's Info.plist | -| `--dry-run` | View the proposed change | -| `-h, --help` | output usage information | - -**Examples** - -- `uri-scheme add com.app` -- `uri-scheme add myapp` - -### remove - -Remove URI schemes from a native app - -**Options** - -| Options | Description | -| -------------------------- | --------------------------------------------------------------- | -| `-a, --android` | Apply action to Android | -| `-i, --ios` | Apply action to iOS | -| `--manifest-path ` | Custom path to use for an Android project's AndroidManifest.xml | -| `--info-path ` | Custom path to use for an iOS project's Info.plist | -| `--dry-run` | View the proposed change | -| `-h, --help` | output usage information | - -**Examples** - -- `uri-scheme remove com.app` -- `uri-scheme remove myapp` - -### open - -Open a URI scheme in a running simulator or emulator - -**Options** - -| Options | Description | -| -------------------- | ----------------------------------------------------------- | -| `-a, --android` | Apply action to Android | -| `-i, --ios` | Apply action to iOS | -| `--package ` | The Android package name to use when opening in an emulator | -| `-h, --help` | output usage information | - -**Examples** - -- `uri-scheme open com.app://oauth --ios` -- `uri-scheme open http://expo.dev --android` - -### list - -List the existing URI scheme prefixes for a native app - -**Options:** - -| Options | Description | -| -------------------------- | --------------------------------------------------------------- | -| `-a, --android` | Apply action to Android | -| `-i, --ios` | Apply action to iOS | -| `--manifest-path ` | Custom path to use for an Android project's AndroidManifest.xml | -| `--info-path ` | Custom path to use for an iOS project's Info.plist | -| `-h, --help` | output usage information | - -## License - -The Expo source code is made available under the [MIT license](LICENSE). Some of the dependencies are licensed differently, with the BSD license, for example. - - - ---- - -

- - - - - License: MIT - -

+This package has [moved to the `expo/expo` repo](https://github.com/expo/expo/tree/main/packages/uri-scheme). diff --git a/packages/uri-scheme/cli.js b/packages/uri-scheme/cli.js deleted file mode 100755 index 7691b2630b..0000000000 --- a/packages/uri-scheme/cli.js +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env node - -const { program } = require('./build'); - -program.parse(process.argv); diff --git a/packages/uri-scheme/jest.config.js b/packages/uri-scheme/jest.config.js deleted file mode 100644 index 1d7ccd2df9..0000000000 --- a/packages/uri-scheme/jest.config.js +++ /dev/null @@ -1,7 +0,0 @@ -const path = require('path'); - -module.exports = { - preset: '../../jest/unit-test-config', - rootDir: path.resolve(__dirname), - displayName: require('./package').name, -}; diff --git a/packages/uri-scheme/package.json b/packages/uri-scheme/package.json deleted file mode 100644 index d07a41f051..0000000000 --- a/packages/uri-scheme/package.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "name": "uri-scheme", - "version": "1.1.0", - "main": "build", - "preferGlobal": true, - "keywords": [ - "expo", - "ios", - "uri-scheme", - "android" - ], - "description": "Interact with native URI schemes", - "repository": { - "type": "git", - "url": "https://github.com/expo/expo-cli.git", - "directory": "packages/uri-scheme" - }, - "author": "Expo ", - "license": "MIT", - "bin": { - "uri-scheme": "./cli.js" - }, - "files": [ - "build", - "cli.js" - ], - "scripts": { - "prepare": "yarn run clean && yarn run build:prod", - "lint": "eslint .", - "watch": "yarn run build -w", - "test": "jest", - "build": "ncc build ./src/index.ts -o build/", - "build:prod": "ncc build ./src/index.ts -o build/ --minify --no-cache --no-source-map-register", - "clean": "rimraf ./build/" - }, - "devDependencies": { - "@expo/config-plugins": "4.1.5", - "@expo/plist": "0.0.20", - "@expo/spawn-async": "1.5.0", - "@types/prompts": "^2.0.6", - "chalk": "^4.0.0", - "commander": "2.20.0", - "glob": "7.1.6", - "prompts": "^2.3.2", - "update-check": "1.5.3" - } -} diff --git a/packages/uri-scheme/src/Android.ts b/packages/uri-scheme/src/Android.ts deleted file mode 100644 index d27fdc463d..0000000000 --- a/packages/uri-scheme/src/Android.ts +++ /dev/null @@ -1,188 +0,0 @@ -import { - AndroidManifest, - readAndroidManifestAsync, - writeAndroidManifestAsync, -} from '@expo/config-plugins/build/android/Manifest'; -import * as Scheme from '@expo/config-plugins/build/android/Scheme'; -import { format } from '@expo/config-plugins/build/utils/XML'; -import spawnAsync from '@expo/spawn-async'; -import chalk from 'chalk'; -import { existsSync } from 'fs'; -import path 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 { - return ( - existsSync(path.join(projectRoot, 'android/app/src/main/AndroidManifest.xml')) || - existsSync(path.join(projectRoot, 'app/src/main/AndroidManifest.xml')) - ); -} - -export async function addAsync({ - dryRun, - uri, - manifestPath, - projectRoot, -}: Pick): Promise { - const resolvedManifestPath = manifestPath ?? getConfigPath(projectRoot); - let manifest = await readConfigAsync(resolvedManifestPath); - - if (!Scheme.ensureManifestHasValidIntentFilter(manifest)) { - throw new CommandError( - `Cannot add scheme "${uri}" because the provided manifest does not have a valid Activity with \`android:launchMode="singleTask"\`.\nThis guide can help you get setup properly https://expo.fyi/setup-android-uri-scheme`, - 'add' - ); - } - if (Scheme.hasScheme(uri, manifest)) { - console.log( - chalk.yellow( - `\u203A Android: URI scheme "${uri}" already exists in AndroidManifest.xml at: ${resolvedManifestPath}` - ) - ); - return false; - } - - manifest = Scheme.appendScheme(uri, manifest); - - if (dryRun) { - console.log(chalk.magenta('Write manifest to: ', resolvedManifestPath)); - console.log(format(manifest)); - return false; - } - await writeConfigAsync(resolvedManifestPath, manifest); - return true; -} - -export async function removeAsync({ - dryRun, - uri, - manifestPath, - projectRoot, -}: Pick): Promise { - const resolvedManifestPath = manifestPath ?? getConfigPath(projectRoot); - let manifest = await readConfigAsync(resolvedManifestPath); - - if (!Scheme.ensureManifestHasValidIntentFilter(manifest)) { - throw new CommandError( - `Cannot remove scheme "${uri}" because the provided manifest does not have a valid Activity with \`android:launchMode="singleTask"\`.\nThis guide can help you get setup properly https://expo.fyi/setup-android-uri-scheme`, - 'remove' - ); - } - - if (!Scheme.hasScheme(uri, manifest)) { - console.log( - chalk.yellow( - `\u203A Android: URI scheme "${uri}" does not exist in AndroidManifest.xml at: ${resolvedManifestPath}` - ) - ); - return false; - } - - manifest = Scheme.removeScheme(uri, manifest); - - if (dryRun) { - console.log(chalk.magenta('Write manifest to: ', resolvedManifestPath)); - console.log(format(manifest)); - return false; - } - await writeConfigAsync(resolvedManifestPath, manifest); - return true; -} - -function whichADB(): string { - if (process.env.ANDROID_HOME) { - return `${process.env.ANDROID_HOME}/platform-tools/adb`; - } - return 'adb'; -} - -export async function getAdbOutputAsync(args: string[]): Promise { - const adb = whichADB(); - - try { - const result = await spawnAsync(adb, args); - return result.stdout; - } catch (e: any) { - const err: string = e.stderr || e.stdout || ''; - let errorMessage = err.trim(); - if (errorMessage.startsWith(BEGINNING_OF_ADB_ERROR_MESSAGE)) { - errorMessage = errorMessage.substring(BEGINNING_OF_ADB_ERROR_MESSAGE.length); - } - throw new CommandError(errorMessage); - } -} - -async function openUrlAsync(...props: (string | null)[]): Promise { - const output = await getAdbOutputAsync([ - 'shell', - 'am', - 'start', - '-a', - 'android.intent.action.VIEW', - '-d', - ...(props.filter(Boolean) as string[]), - ]); - if (output.includes(CANT_START_ACTIVITY_ERROR)) { - throw new CommandError(output.substring(output.indexOf('Error: '))); - } - - return output; -} - -export async function openAsync({ - uri, - androidPackage, -}: Pick & { androidPackage?: string }): Promise { - return await openUrlAsync(uri.replace(/&/g, String.raw`\&`), androidPackage ?? null); -} - -export async function getAsync({ - projectRoot, - manifestPath, -}: Pick): Promise { - const manifest = await readConfigAsync(manifestPath ?? getConfigPath(projectRoot)); - return await Scheme.getSchemesFromManifest(manifest); -} - -function getPackage(androidManifest: AndroidManifest): string | null { - return androidManifest.manifest?.$?.package ?? null; -} - -export async function getProjectIdAsync({ - projectRoot, - manifestPath, -}: Pick): Promise { - const resolvedManifestPath = manifestPath ?? getConfigPath(projectRoot); - const manifest = await readConfigAsync(resolvedManifestPath); - const androidPackage = getPackage(manifest); - if (!androidPackage) - throw new CommandError( - `Android: Failed to resolve android package for Manifest at path: ${resolvedManifestPath}` - ); - return androidPackage; -} - -export function getConfigPath(projectRoot: string): string { - const paths = [ - 'android/app/src/main/AndroidManifest.xml', - 'app/src/main/AndroidManifest.xml', - ].map(relative => path.join(projectRoot, relative)); - for (const manifestPath of paths) { - if (existsSync(manifestPath)) { - return manifestPath; - } - } - throw new Error(`Could not find AndroidManifest.xml, looked in: ${paths.join(', ')}`); -} - -async function readConfigAsync(path: string): Promise { - return await readAndroidManifestAsync(path); -} - -async function writeConfigAsync(path: string, result: any) { - await writeAndroidManifestAsync(path, result); -} diff --git a/packages/uri-scheme/src/CLI.ts b/packages/uri-scheme/src/CLI.ts deleted file mode 100644 index f7856c14ff..0000000000 --- a/packages/uri-scheme/src/CLI.ts +++ /dev/null @@ -1,195 +0,0 @@ -#!/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 * as URIScheme from './URIScheme'; -import shouldUpdate from './update'; - -const packageJson = () => require('../package.json'); - -export const program = new Command(packageJson().name).version(packageJson().version); - -function buildCommand(name: string, examples: string[] = []): Command { - return program - .command(`${name} [uri-protocol]`) - .option('-a, --android', 'Apply action to Android') - .option('-i, --ios', 'Apply action to iOS ') - .on('--help', () => { - if (!examples.length) return; - console.log(); - console.log('Examples:'); - console.log(); - for (const example of examples) { - console.log(` $ uri-scheme ${name} ${example}`); - } - console.log(); - }); -} - -buildCommand('add', ['com.app', 'myapp']) - .description('Add URI schemes to a native app') - .option('-n, --name ', 'Name to use on iOS.') - .option('-r, --role ', 'Role to use on iOS: Editor, Viewer') - .option( - '--manifest-path ', - "Custom path to use for an Android project's AndroidManifest.xml" - ) - .option('--info-path ', "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 { - const options = await parseArgsAsync(uri, args); - await URIScheme.addAsync(options); - shouldUpdate(); - } catch (error) { - commandDidThrowAsync(error); - } - }); - -buildCommand('remove', ['com.app', 'myapp']) - .description('Remove URI schemes from a native app') - .option( - '--manifest-path ', - "Custom path to use for an Android project's AndroidManifest.xml" - ) - .option('--info-path ', "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 { - const options = await parseArgsAsync(uri, args); - await URIScheme.removeAsync(options); - shouldUpdate(); - } catch (error) { - commandDidThrowAsync(error); - } - }); - -buildCommand('open', ['com.app://oauth', 'http://expo.dev']) - .description('Open a URI scheme in a running simulator or emulator') - .option('--package ', 'The Android package name to use when opening in an emulator') - .action(async (uri: string, args: any) => { - try { - if (!args.ios && !args.android) { - throw new CommandError('Please provide a target platform with --ios or --android'); - } - await URIScheme.openAsync({ - projectRoot: process.cwd(), - ...args, - androidPackage: args['package'], - uri, - }); - shouldUpdate(); - } catch (error) { - commandDidThrowAsync(error); - } - }); - -buildCommand('list') - .description('List the existing URI scheme prefixes for a native app') - .option( - '--manifest-path ', - "Custom path to use for an Android project's AndroidManifest.xml" - ) - .option('--info-path ', "Custom path to use for an iOS project's Info.plist") - .action(async (uri: string, args: any) => { - try { - const options = await parseArgsAsync(uri, args); - await URIScheme.listAsync(options); - shouldUpdate(); - } catch (error) { - commandDidThrowAsync(error); - } - }); - -async function parseArgsAsync(uri: string, options: Options): Promise { - 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); - - // This functionality allows users to run `npx uri-scheme add ...` - // in a project with only ios or android without throwing an error. - let canSkipMissingPlatforms = false; - - if (!options.android && !options.ios) { - canSkipMissingPlatforms = true; - for (const key of platforms) { - // @ts-ignore: Set iOS and Android props. - options[key] = true; - } - } else { - if (options.android) { - if (!platforms.includes('android')) { - 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 project not found in directory "${projectRoot}".\nYou can manually select an Info.plist with \`--info-path\`` - ); - } - } - } - - if (options.android && !options.manifestPath) { - try { - options.manifestPath = Android.getConfigPath(projectRoot); - } catch (error) { - if (canSkipMissingPlatforms) { - delete options.android; - } else { - throw error; - } - } - } - if (options.ios && !options.infoPath) { - try { - options.infoPath = Ios.getConfigPath(projectRoot); - } catch (error) { - if (canSkipMissingPlatforms) { - delete options.ios; - } else { - throw error; - } - } - } - - options.uri = uri; - return options; -} - -export function run() { - program.parse(process.argv); -} - -async function commandDidThrowAsync(reason: any) { - console.log(); - if (reason.command) { - console.log( - chalk.red(`\u203A ${chalk.bold(`npx ${packageJson().name} ${reason.command}`)} has failed.`) - ); - console.log(); - } - if (reason.origin === 'uri-scheme') { - console.log(chalk.black.bgRed(reason.message)); - } else { - console.log('Aborting run'); - - console.log(chalk.black.bgRed`An unexpected error was encountered. Please report it as a bug:`); - console.log(reason); - } - console.log(); - - await shouldUpdate(); - - process.exit(1); -} diff --git a/packages/uri-scheme/src/Ios.ts b/packages/uri-scheme/src/Ios.ts deleted file mode 100644 index 118fce74d8..0000000000 --- a/packages/uri-scheme/src/Ios.ts +++ /dev/null @@ -1,139 +0,0 @@ -#!/usr/bin/env node -import * as Scheme from '@expo/config-plugins/build/ios/Scheme'; -import plist from '@expo/plist'; -import spawnAsync from '@expo/spawn-async'; -import chalk from 'chalk'; -import fs from 'fs'; -import { sync as globSync } from 'glob'; -import * as path from 'path'; - -import { CommandError, Options } from './Options'; - -const ignoredPaths = ['**/@(Carthage|Pods|vendor|node_modules)/**']; - -export function isAvailable(projectRoot: string): boolean { - const reactNativeIos = globSync('ios/*.xcodeproj', { - ignore: ignoredPaths, - absolute: true, - cwd: projectRoot, - }); - const currentIos = globSync('*.xcodeproj', { - ignore: ignoredPaths, - absolute: true, - cwd: projectRoot, - }); - return !!currentIos.length || !!reactNativeIos.length; -} - -export async function addAsync({ - uri, - infoPath, - projectRoot, - dryRun, -}: Pick): Promise { - const infoPlistPath = infoPath ?? getConfigPath(projectRoot); - let config = readConfig(infoPlistPath); - - if (Scheme.hasScheme(uri, config)) { - console.log( - chalk.yellow( - `\u203A iOS: URI scheme "${uri}" already exists in Info.plist at: ${infoPlistPath}` - ) - ); - return false; - } - - config = Scheme.appendScheme(uri, config); - - if (dryRun) { - console.log(chalk.magenta('Write plist to: ', infoPlistPath)); - console.log(formatConfig(config)); - return false; - } - writeConfig(infoPlistPath, config); - return true; -} - -export async function removeAsync({ - uri, - infoPath, - projectRoot, - dryRun, -}: Pick): Promise { - const infoPlistPath = infoPath ?? getConfigPath(projectRoot); - - let config = readConfig(infoPlistPath); - - if (!Scheme.hasScheme(uri, config)) { - console.log( - chalk.yellow( - `\u203A iOS: URI scheme "${uri}" does not exist in Info.plist at: ${infoPlistPath}` - ) - ); - return false; - } - - config = Scheme.removeScheme(uri, config); - - if (dryRun) { - console.log(chalk.magenta('Write plist to: ', infoPlistPath)); - console.log(formatConfig(config)); - return false; - } - writeConfig(infoPlistPath, config); - return true; -} - -export async function openAsync({ uri }: Pick): Promise { - await spawnAsync('xcrun', ['simctl', 'openurl', 'booted', uri], { - stdio: 'inherit', - }); -} - -export async function getAsync({ - projectRoot, - infoPath, -}: Pick): Promise { - const infoPlistPath = infoPath ?? getConfigPath(projectRoot); - const rawPlist = fs.readFileSync(infoPlistPath, 'utf8'); - const plistObject = plist.parse(rawPlist); - const schemes = Scheme.getSchemesFromPlist(plistObject); - return schemes; -} - -function getInfoPlistsInDirectory(projectRoot: string): string[] { - // longer name means more suffixes, we want the shortest possible one to be first. - return globSync('*/Info.plist', { ignore: ignoredPaths, absolute: true, cwd: projectRoot }).sort( - (a, b) => a.length - b.length - ); -} - -export function getConfigPath(projectRoot: string): string { - // TODO: Figure out how to avoid using the Tests info.plist - const rnInfoPlistPaths = getInfoPlistsInDirectory(path.join(projectRoot, 'ios')); - if (rnInfoPlistPaths.length) { - return rnInfoPlistPaths[0]; - } - const infoPlistPaths = getInfoPlistsInDirectory(projectRoot); - if (!infoPlistPaths.length) { - throw new CommandError(`iOS: No Info.plist found for project at root: ${projectRoot}`); - } - return infoPlistPaths[0]; -} - -function readConfig(path: string): any { - // Read Plist as source - const rawPlist = fs.readFileSync(path, 'utf8'); - return { ...plist.parse(rawPlist) }; -} - -function formatConfig(plistObject: any): string { - // attempt to match default Info.plist format - const format = { pretty: true, indent: `\t` }; - const xml = plist.build(plistObject, format); - return xml; -} - -function writeConfig(path: string, plistObject: any) { - fs.writeFileSync(path, formatConfig(plistObject)); -} diff --git a/packages/uri-scheme/src/Options.ts b/packages/uri-scheme/src/Options.ts deleted file mode 100644 index 52bcaacc64..0000000000 --- a/packages/uri-scheme/src/Options.ts +++ /dev/null @@ -1,25 +0,0 @@ -export type Options = { - uri: string; - projectRoot: string; - ios?: boolean; - android?: boolean; - 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 { - origin = 'uri-scheme'; - - constructor(message: string, public command?: string) { - super(message); - } -} diff --git a/packages/uri-scheme/src/URIScheme.ts b/packages/uri-scheme/src/URIScheme.ts deleted file mode 100644 index 3632e68ef3..0000000000 --- a/packages/uri-scheme/src/URIScheme.ts +++ /dev/null @@ -1,206 +0,0 @@ -#!/usr/bin/env node -import chalk from 'chalk'; -import { statSync } from 'fs'; -import { relative } from 'path'; -import prompts from 'prompts'; - -import * as Android from './Android'; -import * as Ios from './Ios'; -import { CommandError, Options } from './Options'; - -function fileExists(filePath: string): boolean { - try { - return statSync(filePath).isFile(); - } catch { - return false; - } -} -export function getAvailablePlatforms( - options: Pick -): string[] { - const platforms: string[] = []; - if (options.infoPath) { - if (!fileExists(options.infoPath)) { - throw new CommandError(`Custom Info.plist does not exist at path "${options.infoPath}"`); - } - platforms.push('ios'); - } else if (Ios.isAvailable(options.projectRoot)) { - platforms.push('ios'); - } - if (options.manifestPath) { - if (!fileExists(options.manifestPath)) { - throw new CommandError( - `Custom AndroidManifest.xml does not exist at path "${options.manifestPath}"` - ); - } - platforms.push('android'); - } else if (Android.isAvailable(options.projectRoot)) { - platforms.push('android'); - } - return platforms; -} - -/** - * Ensure the URI scheme is a valid string. - * - * @param uri URI scheme prefix to validate - */ -function ensureUriString(uri: any): string { - if (!uri) { - throw new CommandError('Please supply a URI protocol'); - } - if (typeof uri !== 'string') { - throw new CommandError(`URI protocol should be of type string. Instead got: ${typeof uri}`); - } - - return uri.trim(); -} - -/** - * Normalize a URI scheme prefix according to [RFC 2396](http://www.ietf.org/rfc/rfc2396.txt). - * - * @param uri URI scheme prefix to validate - */ -async function normalizeUriProtocolAsync(uri: any): Promise { - const trimmedUri = ensureUriString(uri); - const [protocol] = trimmedUri.split(':'); - const normalizedUri = protocol.toLowerCase(); - if (normalizedUri !== uri) { - // Create a warning. - if (normalizedUri) { - console.log( - chalk.yellow( - `\u203A Supplied URI protocol "${trimmedUri}" does not match normalized scheme "${normalizedUri}".` - ) - ); - const { answer } = await prompts({ - type: 'confirm', - name: 'answer', - message: `Would you like to use "${normalizedUri}" instead?`, - initial: true, - }); - if (answer) return normalizedUri; - } else { - throw new CommandError( - `Supplied URI protocol "${trimmedUri}" does not appear to be spec compliant: http://www.ietf.org/rfc/rfc2396.txt` - ); - } - } - return trimmedUri; -} - -export async function addAsync(options: Options): Promise { - // Although schemes are case-insensitive, the canonical form is - // lowercase and documents that specify schemes must do so with lowercase letters. - options.uri = await normalizeUriProtocolAsync(options.uri); - - const results: string[] = []; - let actionOccurred = false; - if (options.ios) { - if (await Ios.addAsync(options)) { - actionOccurred = true; - logPlatformMessage('iOS', `Added URI protocol "${options.uri}" to project`); - results.push('ios'); - } - } - if (options.android) { - if (await Android.addAsync(options)) { - actionOccurred = true; - logPlatformMessage('Android', `Added URI protocol "${options.uri}" to project`); - results.push('android'); - } - } - - if (!actionOccurred) { - console.log( - chalk.yellow( - 'No URI schemes could be added. Please ensure there is a native project available.' - ) - ); - } - - return results; -} - -export async function removeAsync(options: Options): Promise { - options.uri = ensureUriString(options.uri); - - const results: string[] = []; - let actionOccurred = false; - - if (options.ios) { - if (await Ios.removeAsync(options)) { - actionOccurred = true; - logPlatformMessage('iOS', `Removed URI protocol "${options.uri}" from project`); - results.push('ios'); - } - } - if (options.android) { - if (await Android.removeAsync(options)) { - actionOccurred = true; - logPlatformMessage('Android', `Removed URI protocol "${options.uri}" from project`); - results.push('android'); - } - } - - if (!actionOccurred) { - console.log( - chalk.yellow( - 'No URI schemes could be removed. Please ensure there is a native project available.' - ) - ); - } - return results; -} - -export async function openAsync( - options: Pick & { androidPackage?: string } -): Promise { - options.uri = ensureUriString(options.uri); - - if (options.ios) { - logPlatformMessage('iOS', `Opening URI "${options.uri}" in simulator`); - await Ios.openAsync(options); - } - if (options.android) { - logPlatformMessage('Android', `Opening URI "${options.uri}" in emulator`); - await Android.openAsync(options); - } -} - -export async function listAsync( - options: Pick -): Promise { - let actionOccurred = false; - - if (options.infoPath) { - actionOccurred = true; - const schemes = await Ios.getAsync(options); - logPlatformMessage( - 'iOS', - `Schemes for config: ./${relative(options.projectRoot, options.infoPath)}` - ); - logSchemes(schemes); - } - if (options.manifestPath) { - actionOccurred = true; - const schemes = await Android.getAsync(options); - logPlatformMessage( - 'Android', - `Schemes for config: ./${relative(options.projectRoot, options.manifestPath)}` - ); - logSchemes(schemes); - } - - if (!actionOccurred) { - console.log(chalk.yellow('Could not find any native URI schemes to list.')); - } -} - -function logPlatformMessage(platform: string, message: string): void { - console.log(chalk.magenta(`\u203A ${chalk.bold(platform)}: ${message}`)); -} -function logSchemes(schemes: string[]): void { - for (const scheme of schemes) console.log(`${chalk.dim('\u203A ')}${scheme}${chalk.dim('://')}`); - console.log(''); -} diff --git a/packages/uri-scheme/src/__tests__/URIScheme-test.ts b/packages/uri-scheme/src/__tests__/URIScheme-test.ts deleted file mode 100644 index b390f6c164..0000000000 --- a/packages/uri-scheme/src/__tests__/URIScheme-test.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { join } from 'path'; - -import * as Android from '../Android'; -import * as Ios from '../Ios'; -import * as URIScheme from '../URIScheme'; - -const projectRoot = join(__dirname, 'fixtures/react-native'); - -it(`cannot add a duplicate uri`, async () => { - const platforms = await URIScheme.addAsync({ - projectRoot, - uri: 'demo.app', - }); - expect(platforms.length).toBe(0); -}); - -it(`cannot remove an invalid uri`, async () => { - const platforms = await URIScheme.removeAsync({ - projectRoot, - uri: 'invalid.value', - }); - expect(platforms.length).toBe(0); -}); - -it(`gets available platforms with default settings`, async () => { - const platforms = await URIScheme.getAvailablePlatforms({ - projectRoot, - }); - expect(platforms).toContain('ios'); - expect(platforms).toContain('android'); -}); - -it(`lists schemes`, async () => { - await URIScheme.listAsync({ - projectRoot, - infoPath: Ios.getConfigPath(projectRoot), - manifestPath: Android.getConfigPath(projectRoot), - }); -}); diff --git a/packages/uri-scheme/src/__tests__/fixtures/react-native/android/app/src/main/AndroidManifest.xml b/packages/uri-scheme/src/__tests__/fixtures/react-native/android/app/src/main/AndroidManifest.xml deleted file mode 100644 index fbb294fc6a..0000000000 --- a/packages/uri-scheme/src/__tests__/fixtures/react-native/android/app/src/main/AndroidManifest.xml +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/packages/uri-scheme/src/__tests__/fixtures/react-native/ios/Info.plist b/packages/uri-scheme/src/__tests__/fixtures/react-native/ios/Info.plist deleted file mode 100644 index d9cc9bf7c6..0000000000 --- a/packages/uri-scheme/src/__tests__/fixtures/react-native/ios/Info.plist +++ /dev/null @@ -1,15 +0,0 @@ - - - - - CFBundleURLTypes - - - CFBundleURLSchemes - - demo.app - - - - - \ No newline at end of file diff --git a/packages/uri-scheme/src/__tests__/fixtures/react-native/ios/demo.xcodeproj b/packages/uri-scheme/src/__tests__/fixtures/react-native/ios/demo.xcodeproj deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/packages/uri-scheme/src/index.ts b/packages/uri-scheme/src/index.ts deleted file mode 100644 index d9a4133f26..0000000000 --- a/packages/uri-scheme/src/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -import * as Android from './Android'; -import * as Ios from './Ios'; -import * as URIScheme from './URIScheme'; -export { program } from './CLI'; - -export { Ios, Android, URIScheme }; diff --git a/packages/uri-scheme/src/update.ts b/packages/uri-scheme/src/update.ts deleted file mode 100644 index bf0a63c516..0000000000 --- a/packages/uri-scheme/src/update.ts +++ /dev/null @@ -1,21 +0,0 @@ -import chalk from 'chalk'; -import checkForUpdate from 'update-check'; - -export default async function shouldUpdate(): Promise { - const packageJson = () => require('../package.json'); - - const update = checkForUpdate(packageJson()).catch(() => null); - - try { - const res = await update; - if (res && res.latest) { - const _packageJson = packageJson(); - console.log(); - console.log(chalk.yellow.bold(`A new version of \`${_packageJson.name}\` is available`)); - console.log('You can update by running: ' + chalk.cyan(`npm i -g ${_packageJson.name}`)); - console.log(); - } - } catch { - // ignore error - } -} diff --git a/packages/uri-scheme/tsconfig.json b/packages/uri-scheme/tsconfig.json deleted file mode 100644 index 88938f641d..0000000000 --- a/packages/uri-scheme/tsconfig.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "extends": "../../tsconfig.base", - "include": ["src/**/*.ts"], - "compilerOptions": { - "resolveJsonModule": true, - "rootDir": "src", - "declaration": false, - "composite": false, - }, - "exclude": ["**/__mocks__/*", "**/__tests__/*"] -}