@react-native-tvos/config-tv 0.0.3 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -4,16 +4,15 @@ Expo Config Plugin to auto-configure the native directories for TV development u
4
4
 
5
5
  _Notes_:
6
6
 
7
- - This is an experimental plugin, tested only against Expo 50 alpha packages.
8
7
  - This package cannot be used in the "Expo Go" app because Expo Go does not support TV.
9
- - Apple TV development will work with many of the commonly used SDK 50 packages, including `expo-updates`, but many Expo packages do not work on Apple TV and are not supported. In particular, `expo-dev-client`, `expo-dev-menu`, and `expo-dev-launcher` are not supported.
8
+ - Apple TV development will work with many of the commonly used SDK 50 packages, including `expo-updates`, but many Expo packages do not work on Apple TV and are not supported. In particular, `expo-dev-client` and `expo-router` are not supported.
10
9
 
11
10
  ## Expo installation
12
11
 
13
12
  - First install the package with yarn, npm, or [`npx expo install`](https://docs.expo.io/workflow/expo-cli/#expo-install).
14
13
 
15
14
  ```sh
16
- npx expo install tv @react-native-tvos/config-tv
15
+ npx expo install @react-native-tvos/config-tv
17
16
  ```
18
17
 
19
18
  After installing this npm package, add the [config plugin](https://docs.expo.io/guides/config-plugins/) to the [`plugins`](https://docs.expo.io/versions/latest/config/app/#plugins) array of your `app.json` or `app.config.js`:
@@ -37,7 +36,9 @@ or
37
36
  {
38
37
  "isTV": true,
39
38
  "showVerboseWarnings": false,
40
- "tvosDeploymentTarget": "13.4"
39
+ "tvosDeploymentTarget": "13.4",
40
+ "removeFlipperOnAndroid": true,
41
+ "androidTVBanner": "assets/images/tv_banner.png"
41
42
  }
42
43
  ]
43
44
  ]
@@ -49,9 +50,14 @@ or
49
50
 
50
51
  _Plugin parameters_:
51
52
 
52
- - `isTV`: (optional boolean, default false) If true, prebuild should generate or modify Android and iOS files to build for TV (Android TV and Apple TV). If false, the default phone-appropriate files should be generated, and if existing files contain TV changes, they will be reverted. Setting the environment variable EXPO_TV to "true" or "1" will override this value and force a TV build.
53
- - `showVerboseWarnings`: (optional boolean, default false) If true, verbose warnings will be shown during plugin execution.
53
+ - `isTV`: (optional boolean, default false) If true, prebuild should generate or modify Android and iOS files to build for TV (Android TV and Apple TV). If false, the plugin will have no effect. Setting the environment variable EXPO_TV to "true" or "1" will override this value and force a TV build.
54
+ - `showVerboseWarnings`: Deprecated. Verbose logging is now shown as in other config plugins, by setting an environment variable:
55
+ - EXPO_DEBUG=1 (shows debug messages from all plugins)
56
+ - DEBUG=expo:\* (shows debug messages from all plugins)
57
+ - DEBUG=expo:react-native-tvos:config-tv (shows debug messages from this plugin only)
54
58
  - `tvosDeploymentTarget`: (optional string, default '13.4') Used to set the tvOS deployment target version in the Xcode project.
59
+ - `removeFlipperOnAndroid`: (optional boolean, default true) Used to remove the Flipper dependency from `MainApplication.kt` (or `MainApplication.java`) and `android/app/build.gradle`. This is necessary for React Native TV 0.73 and higher, since Flipper integration is removed from these versions. If this causes issues, set the value to false, run `npx expo prebuild --clean` again, and then remove Flipper from your Android source manually.
60
+ - `androidTVBanner`: (optional string) If set, this should be a path to an existing PNG file appropriate for an Android TV banner image. See https://developer.android.com/design/ui/tv/guides/system/tv-app-icon-guidelines#banner . The Android manifest will be modified to reference this image, and the image will be copied into Android resource drawable directories.
55
61
 
56
62
  _Warning_:
57
63
 
@@ -60,7 +66,7 @@ When this plugin is used to generate files in the iOS directory that build an Ap
60
66
  ```json
61
67
  {
62
68
  "dependencies": {
63
- "react-native": "npm:react-native-tvos@^0.72.5-0"
69
+ "react-native": "npm:react-native-tvos@^0.73.2-0"
64
70
  }
65
71
  }
66
72
  ```
@@ -69,6 +75,6 @@ If this is not the case, the plugin will run successfully, but Cocoapods install
69
75
 
70
76
  _Warning_:
71
77
 
72
- If you have already generated native directories for a phone build, and set `EXPO_TV` to "true" or "1" (or set the `isTV` plugin parameter to true), then running `npx expo prebuild` will lead to errors when Cocoapods installation is run again. You will need to remove `ios/Pods` and `ios/Podfile.lock` before running Cocoapods installation.
78
+ If you have already generated native directories for a phone build, and set `EXPO_TV` to "true" or "1" (or set the `isTV` plugin parameter to true), then running `npx expo prebuild` again will lead to errors when Cocoapods installation is run again. Similar problems will occur if `EXPO_TV` is set to "0" (or `isTV` set to false) after generating native directories for a TV build.
73
79
 
74
- To avoid this issue, it is strongly recommended to run `npx expo prebuild --clean` if changing the `EXPO_TV` environment variable or the `isTV` plugin parameter.
80
+ To avoid this issue, it is strongly recommended to run `npx expo prebuild --clean` if changing the `EXPO_TV` environment variable or the `isTV` plugin parameter. See [this doc](https://docs.expo.dev/workflow/prebuild/#clean) for more details.
package/build/types.d.ts CHANGED
@@ -3,15 +3,31 @@ export type ConfigData = {
3
3
  * If true, prebuild should generate Android and iOS files for TV (Android TV and Apple TV).
4
4
  * If false, the default phone-appropriate files should be generated.
5
5
  * Setting the environment variable EXPO_TV to "true" or "1" will override
6
- * this value.
6
+ * this value. (Defaults to false.)
7
7
  */
8
8
  isTV?: boolean;
9
9
  /**
10
- * If true, verbose warnings will be shown during plugin execution.
10
+ * Deprecated. Verbose logging is now shown as in other config plugins, by setting an environment variable:
11
+ * EXPO_DEBUG=1
12
+ * or
13
+ * DEBUG=expo:* (shows debug messages from all plugins)
14
+ * or
15
+ * DEBUG=expo:react-native-tvos:config-tv (shows debug messages from this plugin only)
11
16
  */
12
17
  showVerboseWarnings?: boolean;
13
18
  /**
14
19
  * If set, this will be used as the tvOS deployment target version instead of the default (13.4).
15
20
  */
16
21
  tvosDeploymentTarget?: string;
22
+ /**
23
+ * If set, Android code that references Flipper will be removed. (Defaults to true.)
24
+ */
25
+ removeFlipperOnAndroid?: boolean;
26
+ /**
27
+ * If set, this should be a path to an existing PNG file appropriate for an Android TV banner image.
28
+ * See https://developer.android.com/design/ui/tv/guides/system/tv-app-icon-guidelines#banner
29
+ * The Android manifest will be modified to reference this image, and the image will be copied into
30
+ * Android resource drawable directories.
31
+ */
32
+ androidTVBanner?: string;
17
33
  };
package/build/utils.d.ts CHANGED
@@ -1,4 +1,12 @@
1
1
  import { ConfigData } from './types';
2
+ export declare const packageNameAndVersion: string;
2
3
  export declare function isTVEnabled(params: ConfigData): boolean;
3
4
  export declare function showVerboseWarnings(params: ConfigData): boolean;
4
5
  export declare function tvosDeploymentTarget(params: ConfigData): string;
6
+ export declare function shouldRemoveFlipperOnAndroid(params: ConfigData): boolean;
7
+ export declare function androidTVBanner(params: ConfigData): string | undefined;
8
+ export declare function verboseLog(message: string, options?: {
9
+ params?: ConfigData;
10
+ platform?: 'android' | 'ios';
11
+ property?: string;
12
+ }): void;
package/build/utils.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.tvosDeploymentTarget = exports.showVerboseWarnings = exports.isTVEnabled = void 0;
3
+ exports.verboseLog = exports.androidTVBanner = exports.shouldRemoveFlipperOnAndroid = exports.tvosDeploymentTarget = exports.showVerboseWarnings = exports.isTVEnabled = exports.packageNameAndVersion = void 0;
4
4
  const getenv_1 = require("getenv");
5
5
  class Env {
6
6
  /** Enable prebuild for TV */
@@ -8,8 +8,11 @@ class Env {
8
8
  return (0, getenv_1.boolish)('EXPO_TV', false);
9
9
  }
10
10
  }
11
+ const debug = require('debug')('expo:react-native-tvos:config-tv');
11
12
  const env = new Env();
13
+ const pkg = require('../package.json');
12
14
  const defaultTvosDeploymentVersion = '13.4';
15
+ exports.packageNameAndVersion = `${pkg.name}@${pkg.version}`;
13
16
  function isTVEnabled(params) {
14
17
  return env.EXPO_TV || (params?.isTV ?? false);
15
18
  }
@@ -22,3 +25,18 @@ function tvosDeploymentTarget(params) {
22
25
  return params?.tvosDeploymentTarget ?? defaultTvosDeploymentVersion;
23
26
  }
24
27
  exports.tvosDeploymentTarget = tvosDeploymentTarget;
28
+ function shouldRemoveFlipperOnAndroid(params) {
29
+ return params?.removeFlipperOnAndroid ?? true;
30
+ }
31
+ exports.shouldRemoveFlipperOnAndroid = shouldRemoveFlipperOnAndroid;
32
+ function androidTVBanner(params) {
33
+ return params?.androidTVBanner;
34
+ }
35
+ exports.androidTVBanner = androidTVBanner;
36
+ function verboseLog(message, options) {
37
+ const tokens = [message];
38
+ options?.property && tokens.unshift(options?.property);
39
+ options?.platform && tokens.unshift(options?.platform);
40
+ debug(tokens.join(': '));
41
+ }
42
+ exports.verboseLog = verboseLog;
package/build/withTV.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { ConfigPlugin } from "expo/config-plugins";
2
- import { ConfigData } from "./types";
1
+ import { ConfigPlugin } from 'expo/config-plugins';
2
+ import { ConfigData } from './types';
3
3
  declare const _default: ConfigPlugin<ConfigData>;
4
4
  export default _default;
package/build/withTV.js CHANGED
@@ -5,12 +5,26 @@ const withTVAndroidManifest_1 = require("./withTVAndroidManifest");
5
5
  const withTVPodfile_1 = require("./withTVPodfile");
6
6
  const withTVSplashScreen_1 = require("./withTVSplashScreen");
7
7
  const withTVXcodeProject_1 = require("./withTVXcodeProject");
8
+ const withTVAndroidRemoveFlipper_1 = require("./withTVAndroidRemoveFlipper");
9
+ const withTVAndroidBannerImage_1 = require("./withTVAndroidBannerImage");
10
+ const utils_1 = require("./utils");
11
+ const withTVNoEffect = (config, params = {}) => {
12
+ (0, utils_1.verboseLog)(`${utils_1.packageNameAndVersion}: isTV == false, plugin will have no effect`, {});
13
+ return config;
14
+ };
8
15
  const withTVPlugin = (config, params = {}) => {
16
+ const isTV = (0, utils_1.isTVEnabled)(params);
17
+ if (!isTV) {
18
+ config = withTVNoEffect(config, params);
19
+ return config;
20
+ }
9
21
  config = (0, withTVXcodeProject_1.withTVXcodeProject)(config, params);
10
22
  config = (0, withTVPodfile_1.withTVPodfile)(config, params);
11
23
  config = (0, withTVSplashScreen_1.withTVSplashScreen)(config, params);
24
+ config = (0, withTVAndroidBannerImage_1.withTVAndroidBannerImage)(config, params); // This should be done before Android manifest config
12
25
  config = (0, withTVAndroidManifest_1.withTVAndroidManifest)(config, params);
26
+ config = (0, withTVAndroidRemoveFlipper_1.withTVAndroidRemoveFlipper)(config, params);
13
27
  return config;
14
28
  };
15
- const pkg = require("../package.json");
29
+ const pkg = require('../package.json');
16
30
  exports.default = (0, config_plugins_1.createRunOncePlugin)(withTVPlugin, pkg.name, pkg.version);
@@ -0,0 +1,4 @@
1
+ import { ConfigPlugin } from 'expo/config-plugins';
2
+ import { ConfigData } from './types';
3
+ /** Copies TV banner image to the Android resources drawable folders. If image does not exist, throw an exception. */
4
+ export declare const withTVAndroidBannerImage: ConfigPlugin<ConfigData>;
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.withTVAndroidBannerImage = void 0;
7
+ const config_plugins_1 = require("expo/config-plugins");
8
+ const fs_1 = require("fs");
9
+ const path_1 = __importDefault(require("path"));
10
+ const utils_1 = require("./utils");
11
+ const drawableDirectoryNames = [
12
+ 'drawable',
13
+ 'drawable-hdpi',
14
+ 'drawable-mdpi',
15
+ 'drawable-xhdpi',
16
+ 'drawable-xxhdpi',
17
+ 'drawable-xxxhdpi',
18
+ ];
19
+ /** Copies TV banner image to the Android resources drawable folders. If image does not exist, throw an exception. */
20
+ const withTVAndroidBannerImage = (c, params = {}) => {
21
+ const androidTVBannerPath = (0, utils_1.androidTVBanner)(params);
22
+ return (0, config_plugins_1.withDangerousMod)(c, [
23
+ 'android',
24
+ // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
25
+ async (config) => {
26
+ if (!androidTVBannerPath) {
27
+ return config;
28
+ }
29
+ (0, utils_1.verboseLog)(`adding TV banner image ${androidTVBannerPath} to Android resources`, {
30
+ params,
31
+ platform: 'android',
32
+ property: 'manifest',
33
+ });
34
+ for (const drawableDirectoryName of drawableDirectoryNames) {
35
+ const drawableDirectoryPath = path_1.default.join(config.modRequest.platformProjectRoot, 'app', 'src', 'main', 'res', drawableDirectoryName);
36
+ if (!(0, fs_1.existsSync)(drawableDirectoryPath)) {
37
+ await fs_1.promises.mkdir(drawableDirectoryPath);
38
+ }
39
+ await fs_1.promises.copyFile(androidTVBannerPath, path_1.default.join(drawableDirectoryPath, 'tv_banner.png'));
40
+ }
41
+ return config;
42
+ },
43
+ ]);
44
+ };
45
+ exports.withTVAndroidBannerImage = withTVAndroidBannerImage;
@@ -1,6 +1,7 @@
1
- import { ExpoConfig } from "expo/config";
2
- import { AndroidConfig, ConfigPlugin } from "expo/config-plugins";
3
- import { ConfigData } from "./types";
1
+ import { ExpoConfig } from 'expo/config';
2
+ import { AndroidConfig, ConfigPlugin } from 'expo/config-plugins';
3
+ import { ConfigData } from './types';
4
4
  export declare const withTVAndroidManifest: ConfigPlugin<ConfigData>;
5
- export declare function setLeanBackLauncherIntent(_config: Pick<ExpoConfig, "android">, androidManifest: AndroidConfig.Manifest.AndroidManifest, verbose: boolean): AndroidConfig.Manifest.AndroidManifest;
6
- export declare function removePortraitOrientation(_config: Pick<ExpoConfig, "android">, androidManifest: AndroidConfig.Manifest.AndroidManifest, verbose: boolean): Promise<AndroidConfig.Manifest.AndroidManifest>;
5
+ export declare function setLeanBackLauncherIntent(_config: Pick<ExpoConfig, 'android'>, androidManifest: AndroidConfig.Manifest.AndroidManifest, params: ConfigData): AndroidConfig.Manifest.AndroidManifest;
6
+ export declare function removePortraitOrientation(_config: Pick<ExpoConfig, 'android'>, androidManifest: AndroidConfig.Manifest.AndroidManifest, params: ConfigData): AndroidConfig.Manifest.AndroidManifest;
7
+ export declare function setTVBanner(_config: Pick<ExpoConfig, 'android'>, androidManifest: AndroidConfig.Manifest.AndroidManifest, params: ConfigData, androidTVBannerPath: string | undefined): AndroidConfig.Manifest.AndroidManifest;
@@ -1,56 +1,55 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.removePortraitOrientation = exports.setLeanBackLauncherIntent = exports.withTVAndroidManifest = void 0;
3
+ exports.setTVBanner = exports.removePortraitOrientation = exports.setLeanBackLauncherIntent = exports.withTVAndroidManifest = void 0;
4
4
  const config_plugins_1 = require("expo/config-plugins");
5
5
  const utils_1 = require("./utils");
6
- const pkg = require("../package.json");
7
- const { getMainActivity } = config_plugins_1.AndroidConfig.Manifest;
6
+ const { getMainActivity, getMainApplication } = config_plugins_1.AndroidConfig.Manifest;
8
7
  const withTVAndroidManifest = (config, params = {}) => {
9
- const isTV = (0, utils_1.isTVEnabled)(params);
10
- const verbose = (0, utils_1.showVerboseWarnings)(params);
11
- return (0, config_plugins_1.withAndroidManifest)(config, async (config) => {
12
- if (!isTV) {
13
- // nothing to do
14
- return config;
8
+ const androidTVBannerPath = (0, utils_1.androidTVBanner)(params);
9
+ return (0, config_plugins_1.withAndroidManifest)(config, (config) => {
10
+ config.modResults = setLeanBackLauncherIntent(config, config.modResults, params);
11
+ config.modResults = removePortraitOrientation(config, config.modResults, params);
12
+ if (androidTVBannerPath) {
13
+ config.modResults = setTVBanner(config, config.modResults, params, androidTVBannerPath);
15
14
  }
16
- config.modResults = await setLeanBackLauncherIntent(config, config.modResults, verbose);
17
- config.modResults = await removePortraitOrientation(config, config.modResults, verbose);
18
15
  return config;
19
16
  });
20
17
  };
21
18
  exports.withTVAndroidManifest = withTVAndroidManifest;
22
- const LEANBACK_LAUNCHER_CATEGORY = "android.intent.category.LEANBACK_LAUNCHER";
19
+ const LEANBACK_LAUNCHER_CATEGORY = 'android.intent.category.LEANBACK_LAUNCHER';
23
20
  function getMainLaunchIntent(androidManifest) {
24
21
  const mainActivity = getMainActivity(androidManifest);
25
- const intentFilters = mainActivity?.["intent-filter"];
22
+ const intentFilters = mainActivity?.['intent-filter'];
26
23
  const mainLaunchIntents = (intentFilters ?? []).filter((i) => {
27
24
  const action = i.action ?? [];
28
25
  if (action.length === 0) {
29
26
  return false;
30
27
  }
31
- return action[0]?.$["android:name"] === "android.intent.action.MAIN";
28
+ return action[0]?.$['android:name'] === 'android.intent.action.MAIN';
32
29
  });
33
30
  return mainLaunchIntents.length ? mainLaunchIntents[0] : undefined;
34
31
  }
35
32
  function leanbackLauncherCategoryExistsInMainLaunchIntent(mainLaunchIntent) {
36
33
  const mainLaunchCategories = mainLaunchIntent.category ?? [];
37
- const mainLaunchIntentCategoriesWithLeanbackLauncher = mainLaunchCategories.filter((c) => c.$["android:name"] === LEANBACK_LAUNCHER_CATEGORY);
34
+ const mainLaunchIntentCategoriesWithLeanbackLauncher = mainLaunchCategories.filter((c) => c.$['android:name'] === LEANBACK_LAUNCHER_CATEGORY);
38
35
  return mainLaunchIntentCategoriesWithLeanbackLauncher.length > 0;
39
36
  }
40
- function setLeanBackLauncherIntent(_config, androidManifest, verbose) {
37
+ function setLeanBackLauncherIntent(_config, androidManifest, params) {
41
38
  const mainLaunchIntent = getMainLaunchIntent(androidManifest);
42
39
  if (!mainLaunchIntent) {
43
- throw new Error(`${pkg.name}@${pkg.version}: no main intent in main activity of Android manifest`);
40
+ throw new Error(`${utils_1.packageNameAndVersion}: no main intent in main activity of Android manifest`);
44
41
  }
45
42
  if (!leanbackLauncherCategoryExistsInMainLaunchIntent(mainLaunchIntent)) {
46
43
  // Leanback needs to be added
47
- if (verbose) {
48
- config_plugins_1.WarningAggregator.addWarningAndroid("manifest", `${pkg.name}@${pkg.version}: adding TV leanback launcher category to main intent in AndroidManifest.xml`);
49
- }
44
+ (0, utils_1.verboseLog)('adding TV leanback launcher category to main intent in AndroidManifest.xml', {
45
+ params,
46
+ platform: 'android',
47
+ property: 'manifest',
48
+ });
50
49
  const mainLaunchCategories = mainLaunchIntent.category ?? [];
51
50
  mainLaunchCategories.push({
52
51
  $: {
53
- "android:name": LEANBACK_LAUNCHER_CATEGORY,
52
+ 'android:name': LEANBACK_LAUNCHER_CATEGORY,
54
53
  },
55
54
  });
56
55
  mainLaunchIntent.category = mainLaunchCategories;
@@ -58,17 +57,36 @@ function setLeanBackLauncherIntent(_config, androidManifest, verbose) {
58
57
  return androidManifest;
59
58
  }
60
59
  exports.setLeanBackLauncherIntent = setLeanBackLauncherIntent;
61
- async function removePortraitOrientation(_config, androidManifest, verbose) {
60
+ function removePortraitOrientation(_config, androidManifest, params) {
62
61
  const mainActivity = getMainActivity(androidManifest);
63
62
  if (mainActivity?.$) {
64
63
  const metadata = mainActivity?.$ ?? {};
65
- if (metadata["android:screenOrientation"]) {
66
- if (verbose) {
67
- config_plugins_1.WarningAggregator.addWarningAndroid("manifest", `${pkg.name}@${pkg.version}: removing screen orientation from AndroidManifest.xml`);
68
- }
69
- delete metadata["android:screenOrientation"];
64
+ if (metadata['android:screenOrientation']) {
65
+ (0, utils_1.verboseLog)('removing screen orientation from AndroidManifest.xml', {
66
+ params,
67
+ platform: 'android',
68
+ property: 'manifest',
69
+ });
70
+ delete metadata['android:screenOrientation'];
70
71
  }
71
72
  }
72
73
  return androidManifest;
73
74
  }
74
75
  exports.removePortraitOrientation = removePortraitOrientation;
76
+ function setTVBanner(_config, androidManifest, params, androidTVBannerPath) {
77
+ if (!androidTVBannerPath) {
78
+ return androidManifest;
79
+ }
80
+ const mainApplication = getMainApplication(androidManifest);
81
+ if (mainApplication?.$) {
82
+ const metadata = mainApplication?.$ ?? {};
83
+ (0, utils_1.verboseLog)('adding TV banner to AndroidManifest.xml', {
84
+ params,
85
+ platform: 'android',
86
+ property: 'manifest',
87
+ });
88
+ metadata['android:banner'] = '@drawable/tv_banner';
89
+ }
90
+ return androidManifest;
91
+ }
92
+ exports.setTVBanner = setTVBanner;
@@ -0,0 +1,4 @@
1
+ import { ConfigPlugin } from 'expo/config-plugins';
2
+ import { ConfigData } from './types';
3
+ /** Dangerously makes or reverts TV changes in the project Podfile. */
4
+ export declare const withTVAndroidRemoveFlipper: ConfigPlugin<ConfigData>;
@@ -0,0 +1,66 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.withTVAndroidRemoveFlipper = void 0;
7
+ const config_plugins_1 = require("expo/config-plugins");
8
+ const fs_1 = require("fs");
9
+ const glob_1 = __importDefault(require("glob"));
10
+ const path_1 = __importDefault(require("path"));
11
+ const utils_1 = require("./utils");
12
+ /** Dangerously makes or reverts TV changes in the project Podfile. */
13
+ const withTVAndroidRemoveFlipper = (c, params = {}) => {
14
+ const androidRemoveFlipper = (0, utils_1.shouldRemoveFlipperOnAndroid)(params);
15
+ return (0, config_plugins_1.withDangerousMod)(c, [
16
+ 'android',
17
+ // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
18
+ async (config) => {
19
+ if (androidRemoveFlipper) {
20
+ // Modify main application
21
+ const mainApplicationFile = mainApplicationFilePath(config.modRequest.platformProjectRoot);
22
+ (0, utils_1.verboseLog)('removing Flipper from MainApplication file', {
23
+ params,
24
+ platform: 'android',
25
+ property: 'manifest',
26
+ });
27
+ const mainApplicationContents = await fs_1.promises.readFile(mainApplicationFile, 'utf8');
28
+ const mainApplicationModifiedContents = commentOutLinesWithString(mainApplicationContents, 'Flipper');
29
+ await fs_1.promises.writeFile(mainApplicationFile, mainApplicationModifiedContents, 'utf-8');
30
+ // Modify app/build.gradle
31
+ const buildGradleFile = appBuildGradleFilePath(config.modRequest.platformProjectRoot);
32
+ (0, utils_1.verboseLog)('removing Flipper from android/app/build.gradle', {
33
+ params,
34
+ platform: 'android',
35
+ property: 'manifest',
36
+ });
37
+ const buildGradleContents = await fs_1.promises.readFile(buildGradleFile, 'utf-8');
38
+ const buildGradleModifiedContents = commentOutLinesWithString(buildGradleContents, 'flipper');
39
+ await fs_1.promises.writeFile(buildGradleFile, buildGradleModifiedContents, 'utf-8');
40
+ }
41
+ return config;
42
+ },
43
+ ]);
44
+ };
45
+ exports.withTVAndroidRemoveFlipper = withTVAndroidRemoveFlipper;
46
+ const mainApplicationFilePath = (androidRoot) => {
47
+ const paths = glob_1.default.sync(`${androidRoot}/**/MainApplication.*`);
48
+ if (paths.length > 0) {
49
+ return paths[0];
50
+ }
51
+ else {
52
+ throw new Error('AndroidApplication path not found');
53
+ }
54
+ };
55
+ const appBuildGradleFilePath = (androidRoot) => path_1.default.resolve(androidRoot, 'app', 'build.gradle');
56
+ const commentOutLinesWithString = (contents, searchString) => {
57
+ return contents
58
+ .split('\n')
59
+ .map((line) => {
60
+ if (line.indexOf(searchString) !== -1) {
61
+ return line.replace(/^/, '// ');
62
+ }
63
+ return line;
64
+ })
65
+ .join('\n');
66
+ };
@@ -1,6 +1,5 @@
1
- import { ConfigPlugin } from "expo/config-plugins";
2
- import { ConfigData } from "./types";
1
+ import { ConfigPlugin } from 'expo/config-plugins';
2
+ import { ConfigData } from './types';
3
3
  /** Dangerously makes or reverts TV changes in the project Podfile. */
4
4
  export declare const withTVPodfile: ConfigPlugin<ConfigData>;
5
- export declare function removeTVPodfileModifications(src: string): string;
6
5
  export declare function addTVPodfileModifications(src: string): string;
@@ -3,55 +3,35 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.addTVPodfileModifications = exports.removeTVPodfileModifications = exports.withTVPodfile = void 0;
6
+ exports.addTVPodfileModifications = exports.withTVPodfile = void 0;
7
7
  const generateCode_1 = require("@expo/config-plugins/build/utils/generateCode");
8
8
  const config_plugins_1 = require("expo/config-plugins");
9
9
  const fs_1 = require("fs");
10
10
  const path_1 = __importDefault(require("path"));
11
11
  const utils_1 = require("./utils");
12
- const pkg = require("../package.json");
13
12
  /** Dangerously makes or reverts TV changes in the project Podfile. */
14
13
  const withTVPodfile = (c, params = {}) => {
15
- const isTV = (0, utils_1.isTVEnabled)(params);
16
- const verbose = (0, utils_1.showVerboseWarnings)(params);
17
14
  return (0, config_plugins_1.withDangerousMod)(c, [
18
- "ios",
15
+ 'ios',
19
16
  // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
20
17
  async (config) => {
21
- const file = path_1.default.join(config.modRequest.platformProjectRoot, "Podfile");
22
- const contents = await fs_1.promises.readFile(file, "utf8");
23
- const modifiedContents = isTV
24
- ? addTVPodfileModifications(contents)
25
- : removeTVPodfileModifications(contents);
26
- if (modifiedContents !== contents) {
27
- if (verbose) {
28
- config_plugins_1.WarningAggregator.addWarningIOS("podfile", `${pkg.name}@${pkg.version}: modifying Podfile for ${isTV ? "tvOS" : "iOS"}`);
29
- }
30
- await fs_1.promises.writeFile(file, modifiedContents, "utf-8");
31
- }
18
+ const file = path_1.default.join(config.modRequest.platformProjectRoot, 'Podfile');
19
+ const contents = await fs_1.promises.readFile(file, 'utf8');
20
+ const modifiedContents = addTVPodfileModifications(contents);
21
+ (0, utils_1.verboseLog)('modifying Podfile for tvOS', {
22
+ params,
23
+ platform: 'ios',
24
+ property: 'podfile',
25
+ });
26
+ await fs_1.promises.writeFile(file, modifiedContents, 'utf-8');
32
27
  return config;
33
28
  },
34
29
  ]);
35
30
  };
36
31
  exports.withTVPodfile = withTVPodfile;
37
- const MOD_TAG = "react-native-tvos-import";
38
- function removeTVPodfileModifications(src) {
39
- if (src.indexOf("platform :tvos") === -1) {
40
- return src;
41
- }
42
- const intermediateSrc = src.replace("platform :tvos", "platform :ios");
43
- const newSrc = (0, generateCode_1.removeContents)({
44
- src: intermediateSrc,
45
- tag: MOD_TAG,
46
- }).contents;
47
- if (!newSrc) {
48
- throw new Error(`${pkg.name}@${pkg.version}: Error in removing TV modifications from Podfile. Recommend running "npx expo prebuild --clean"`);
49
- }
50
- return newSrc;
51
- }
52
- exports.removeTVPodfileModifications = removeTVPodfileModifications;
32
+ const MOD_TAG = 'react-native-tvos-import';
53
33
  function addTVPodfileModifications(src) {
54
- if (src.indexOf("platform :tvos") !== -1) {
34
+ if (src.indexOf('platform :tvos') !== -1) {
55
35
  return src;
56
36
  }
57
37
  const newSrc = (0, generateCode_1.mergeContents)({
@@ -60,8 +40,8 @@ function addTVPodfileModifications(src) {
60
40
  newSrc: "source 'https://github.com/react-native-tvos/react-native-tvos-podspecs.git'\nsource 'https://cdn.cocoapods.org/'\n",
61
41
  anchor: /^/,
62
42
  offset: 0,
63
- comment: "#",
43
+ comment: '#',
64
44
  }).contents;
65
- return newSrc.replace("platform :ios", "platform :tvos");
45
+ return newSrc.replace('platform :ios', 'platform :tvos');
66
46
  }
67
47
  exports.addTVPodfileModifications = addTVPodfileModifications;
@@ -1,6 +1,5 @@
1
- import { ConfigPlugin } from "expo/config-plugins";
2
- import { ConfigData } from "./types";
3
- /** Dangerously modifies or reverts changes needed for TV in SplashScreen.storyboard. */
1
+ import { ConfigPlugin } from 'expo/config-plugins';
2
+ import { ConfigData } from './types';
3
+ /** Dangerously makes changes needed for TV in SplashScreen.storyboard. */
4
4
  export declare const withTVSplashScreen: ConfigPlugin<ConfigData>;
5
5
  export declare function addTVSplashScreenModifications(src: string): string;
6
- export declare function removeTVSplashScreenModifications(src: string): string;
@@ -3,47 +3,42 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.removeTVSplashScreenModifications = exports.addTVSplashScreenModifications = exports.withTVSplashScreen = void 0;
6
+ exports.addTVSplashScreenModifications = exports.withTVSplashScreen = void 0;
7
7
  const config_plugins_1 = require("expo/config-plugins");
8
8
  const fs_1 = require("fs");
9
9
  const path_1 = __importDefault(require("path"));
10
10
  const utils_1 = require("./utils");
11
- const pkg = require("../package.json");
12
- /** Dangerously modifies or reverts changes needed for TV in SplashScreen.storyboard. */
11
+ /** Dangerously makes changes needed for TV in SplashScreen.storyboard. */
13
12
  const withTVSplashScreen = (config, params = {}) => {
14
- const isTV = (0, utils_1.isTVEnabled)(params);
15
- const verbose = (0, utils_1.showVerboseWarnings)(params);
16
13
  return (0, config_plugins_1.withDangerousMod)(config, [
17
- "ios",
14
+ 'ios',
18
15
  // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
19
16
  async (config) => {
20
17
  if (!config.modRequest.projectName) {
21
- throw new Error(`The ${pkg.name}@${pkg.version} plugin requires a configured project name.`);
22
- }
23
- const file = path_1.default.join(config.modRequest.platformProjectRoot, config.modRequest.projectName, "SplashScreen.storyboard");
24
- const contents = await fs_1.promises.readFile(file, "utf8");
25
- const modifiedContents = isTV
26
- ? addTVSplashScreenModifications(contents)
27
- : removeTVSplashScreenModifications(contents);
28
- if (modifiedContents !== contents) {
29
- if (verbose) {
30
- config_plugins_1.WarningAggregator.addWarningIOS("splashscreen", `${pkg.name}@${pkg.version}:: modifying SplashScreen.storyboard for ${isTV ? "tvOS" : "iOS"}`);
31
- }
32
- await fs_1.promises.writeFile(file, modifiedContents, "utf-8");
18
+ throw new Error(`The ${utils_1.packageNameAndVersion} plugin requires a configured project name.`);
33
19
  }
20
+ const file = path_1.default.join(config.modRequest.platformProjectRoot, config.modRequest.projectName, 'SplashScreen.storyboard');
21
+ const contents = await fs_1.promises.readFile(file, 'utf8');
22
+ const modifiedContents = addTVSplashScreenModifications(contents);
23
+ (0, utils_1.verboseLog)('modifying SplashScreen.storyboard for tvOS', {
24
+ params,
25
+ platform: 'ios',
26
+ property: 'splashscreen',
27
+ });
28
+ await fs_1.promises.writeFile(file, modifiedContents, 'utf-8');
34
29
  return config;
35
30
  },
36
31
  ]);
37
32
  };
38
33
  exports.withTVSplashScreen = withTVSplashScreen;
39
34
  const splashScreenStringsForPhone = [
40
- "com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB",
35
+ 'com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB',
41
36
  'targetRuntime="iOS.CocoaTouch"',
42
37
  'id="retina5_5"',
43
38
  '<deployment identifier="iOS"/>',
44
39
  ];
45
40
  const splashScreenStringsForTV = [
46
- "com.apple.InterfaceBuilder.AppleTV.Storyboard",
41
+ 'com.apple.InterfaceBuilder.AppleTV.Storyboard',
47
42
  'targetRuntime="AppleTV"',
48
43
  'id="appleTV"',
49
44
  '<deployment identifier="tvOS"/>',
@@ -51,7 +46,7 @@ const splashScreenStringsForTV = [
51
46
  function modifySource(src, originalStrings, replacementStrings) {
52
47
  let modifiedSource = src;
53
48
  originalStrings.forEach((s, i) => {
54
- const original = new RegExp(`${s}`, "g");
49
+ const original = new RegExp(`${s}`, 'g');
55
50
  const replacement = replacementStrings[i];
56
51
  modifiedSource = modifiedSource.replace(original, replacement);
57
52
  });
@@ -61,7 +56,3 @@ function addTVSplashScreenModifications(src) {
61
56
  return modifySource(src, splashScreenStringsForPhone, splashScreenStringsForTV);
62
57
  }
63
58
  exports.addTVSplashScreenModifications = addTVSplashScreenModifications;
64
- function removeTVSplashScreenModifications(src) {
65
- return modifySource(src, splashScreenStringsForTV, splashScreenStringsForPhone);
66
- }
67
- exports.removeTVSplashScreenModifications = removeTVSplashScreenModifications;
@@ -2,18 +2,8 @@ import { ExpoConfig } from '@expo/config-types';
2
2
  import { ConfigPlugin, XcodeProject } from 'expo/config-plugins';
3
3
  import { ConfigData } from './types';
4
4
  export declare const withTVXcodeProject: ConfigPlugin<ConfigData>;
5
- export declare function setXcodeProjectBuildSettings(config: Pick<ExpoConfig, 'ios'>, { project, isTV, verbose, deploymentTarget, }: {
5
+ export declare function setXcodeProjectBuildSettings(config: Pick<ExpoConfig, 'ios'>, { project, params, deploymentTarget, }: {
6
6
  project: XcodeProject;
7
- isTV: boolean;
8
- verbose: boolean;
7
+ params: ConfigData;
9
8
  deploymentTarget: string;
10
9
  }): XcodeProject;
11
- /**
12
- * Wrapping the families in double quotes is the only way to set a value with a comma in it.
13
- *
14
- * @param deviceFamilies
15
- */
16
- export declare function formatDeviceFamilies(deviceFamilies: number[]): string;
17
- export declare function getSupportsTablet(config: Pick<ExpoConfig, 'ios'>): boolean;
18
- export declare function getIsTabletOnly(config: Pick<ExpoConfig, 'ios'>): boolean;
19
- export declare function getDeviceFamilies(config: Pick<ExpoConfig, 'ios'>): number[];
@@ -1,36 +1,33 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getDeviceFamilies = exports.getIsTabletOnly = exports.getSupportsTablet = exports.formatDeviceFamilies = exports.setXcodeProjectBuildSettings = exports.withTVXcodeProject = void 0;
3
+ exports.setXcodeProjectBuildSettings = exports.withTVXcodeProject = void 0;
4
4
  const config_plugins_1 = require("expo/config-plugins");
5
5
  const utils_1 = require("./utils");
6
- const pkg = require('../package.json');
7
6
  const withTVXcodeProject = (config, params) => {
8
- const isTV = (0, utils_1.isTVEnabled)(params);
9
- const verbose = (0, utils_1.showVerboseWarnings)(params);
10
7
  const deploymentTarget = (0, utils_1.tvosDeploymentTarget)(params);
11
8
  return (0, config_plugins_1.withXcodeProject)(config, async (config) => {
12
9
  config.modResults = await setXcodeProjectBuildSettings(config, {
13
10
  project: config.modResults,
14
- isTV,
15
- verbose,
11
+ params,
16
12
  deploymentTarget,
17
13
  });
18
14
  return config;
19
15
  });
20
16
  };
21
17
  exports.withTVXcodeProject = withTVXcodeProject;
22
- function setXcodeProjectBuildSettings(config, { project, isTV, verbose, deploymentTarget, }) {
23
- const deviceFamilies = formatDeviceFamilies(getDeviceFamilies(config));
18
+ function setXcodeProjectBuildSettings(config, { project, params, deploymentTarget, }) {
24
19
  const configurations = project.pbxXCBuildConfigurationSection();
25
20
  // @ts-ignore
26
21
  for (const { buildSettings } of Object.values(configurations || {})) {
27
22
  // Guessing that this is the best way to emulate Xcode.
28
23
  // Using `project.addToBuildSettings` modifies too many targets.
29
24
  if (typeof buildSettings?.PRODUCT_NAME !== 'undefined') {
30
- if (isTV && buildSettings.TARGETED_DEVICE_FAMILY !== '3') {
31
- if (verbose) {
32
- config_plugins_1.WarningAggregator.addWarningIOS('xcodeproject', `${pkg.name}@${pkg.version}: modifying target ${buildSettings?.PRODUCT_NAME} for ${isTV ? 'tvOS' : 'iOS'}`);
33
- }
25
+ if (buildSettings.TARGETED_DEVICE_FAMILY !== '3') {
26
+ (0, utils_1.verboseLog)(`modifying target ${buildSettings?.PRODUCT_NAME} for tvOS`, {
27
+ params,
28
+ platform: 'ios',
29
+ property: 'xcodeproject',
30
+ });
34
31
  buildSettings.TARGETED_DEVICE_FAMILY = '3';
35
32
  buildSettings.TVOS_DEPLOYMENT_TARGET = deploymentTarget;
36
33
  buildSettings.SDKROOT = 'appletvos';
@@ -38,53 +35,8 @@ function setXcodeProjectBuildSettings(config, { project, isTV, verbose, deployme
38
35
  delete buildSettings?.IOS_DEPLOYMENT_TARGET;
39
36
  }
40
37
  }
41
- else if (!isTV && buildSettings.TARGETED_DEVICE_FAMILY === '3') {
42
- config_plugins_1.WarningAggregator.addWarningIOS('xcodeproject', `${pkg.name}@${pkg.version}: modifying target ${buildSettings?.PRODUCT_NAME} for ${isTV ? 'tvOS' : 'iOS'}`);
43
- buildSettings.TARGETED_DEVICE_FAMILY = deviceFamilies;
44
- buildSettings.IOS_DEPLOYMENT_TARGET = deploymentTarget;
45
- buildSettings.SDKROOT = 'iphoneos';
46
- if (typeof buildSettings?.TVOS_DEPLOYMENT_TARGET !== 'undefined') {
47
- delete buildSettings?.TVOS_DEPLOYMENT_TARGET;
48
- }
49
- }
50
38
  }
51
39
  }
52
40
  return project;
53
41
  }
54
42
  exports.setXcodeProjectBuildSettings = setXcodeProjectBuildSettings;
55
- /**
56
- * Wrapping the families in double quotes is the only way to set a value with a comma in it.
57
- *
58
- * @param deviceFamilies
59
- */
60
- function formatDeviceFamilies(deviceFamilies) {
61
- return `"${deviceFamilies.join(',')}"`;
62
- }
63
- exports.formatDeviceFamilies = formatDeviceFamilies;
64
- function getSupportsTablet(config) {
65
- return !!config.ios?.supportsTablet;
66
- }
67
- exports.getSupportsTablet = getSupportsTablet;
68
- function getIsTabletOnly(config) {
69
- return !!config?.ios?.isTabletOnly;
70
- }
71
- exports.getIsTabletOnly = getIsTabletOnly;
72
- function getDeviceFamilies(config) {
73
- const supportsTablet = getSupportsTablet(config);
74
- const isTabletOnly = getIsTabletOnly(config);
75
- if (isTabletOnly && config.ios?.supportsTablet === false) {
76
- // add warning
77
- }
78
- // 1 is iPhone, 2 is iPad
79
- if (isTabletOnly) {
80
- return [2];
81
- }
82
- else if (supportsTablet) {
83
- return [1, 2];
84
- }
85
- else {
86
- // is iPhone only
87
- return [1];
88
- }
89
- }
90
- exports.getDeviceFamilies = getDeviceFamilies;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@react-native-tvos/config-tv",
3
- "version": "0.0.3",
3
+ "version": "0.0.5",
4
4
  "description": "Config plugin to reconfigure native directories for Apple TV and Android TV development if needed",
5
5
  "main": "build/withTV.js",
6
6
  "types": "build/withTV.d.ts",
@@ -34,7 +34,7 @@
34
34
  "react-native-tvos"
35
35
  ],
36
36
  "peerDependencies": {
37
- "expo": "^50.0.0.alpha.5"
37
+ "expo": "^50.0.6"
38
38
  },
39
39
  "devDependencies": {
40
40
  "@types/getenv": "^1.0.1",