@react-native-tvos/config-tv 0.0.4 → 0.0.6

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`:
@@ -38,7 +37,16 @@ or
38
37
  "isTV": true,
39
38
  "showVerboseWarnings": false,
40
39
  "tvosDeploymentTarget": "13.4",
41
- "removeFlipperOnAndroid": true
40
+ "removeFlipperOnAndroid": true,
41
+ "androidTVBanner": "assets/images/tv_banner.png",
42
+ "appleTVImages": {
43
+ "icon": "./assets/images/myimage-tvos-1280x768.png",
44
+ "iconSmall": "./assets/images/myimage-tvos-400x240.png",
45
+ "topShelf": "./assets/images/myimage-tvos-1920x720.png",
46
+ "topShelf2x": "./assets/images/myimage-tvos-3840x1440.png",
47
+ "topShelfWide": "./assets/images/myimage-tvos-2320x720.png",
48
+ "topShelfWide2x": "./assets/images/myimage-tvos-4640x1440.png"
49
+ }
42
50
  }
43
51
  ]
44
52
  ]
@@ -50,10 +58,21 @@ or
50
58
 
51
59
  _Plugin parameters_:
52
60
 
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 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.
54
- - `showVerboseWarnings`: (optional boolean, default false) If true, verbose warnings will be shown during plugin execution.
61
+ - `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.
62
+ - `showVerboseWarnings`: Deprecated. Verbose logging is now shown as in other config plugins, by setting an environment variable:
63
+ - `EXPO_DEBUG=1` (shows debug messages from all plugins)
64
+ - `DEBUG=expo:*` (shows debug messages from all plugins)
65
+ - `DEBUG=expo:react-native-tvos:config-tv` (shows debug messages from this plugin only)
55
66
  - `tvosDeploymentTarget`: (optional string, default '13.4') Used to set the tvOS deployment target version in the Xcode project.
56
- - `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 `prebuild --clean` again, and then remove Flipper from your Android source manually.
67
+ - `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.
68
+ - `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.
69
+ - `appleTVImages`: (optional object) If set, this is an object with the paths to images needed to construct the Apple TV icon and top shelf brand assets. The images will be used to construct a brand asset catalog in the Xcode project Image catalog, and the project updated to use the brand assets as the source for the app icons. If this property is set, all six image paths must be defined and the files must exist, or an error will be thrown. The images need to be the exact sizes shown here, in order to avoid errors during Xcode compilation and on submission to the App Store or TestFlight.
70
+ - `icon`: (string) Path to a 1280x760 image
71
+ - `iconSmall`: (string) Path to a 400x240 image
72
+ - `topShelf`: (string) Path to a 1920x720 image
73
+ - `topShelf2x`: (string) Path to a 3840x1440 image
74
+ - `topShelfWide`: (string) Path to a 2320x720 image
75
+ - `topShelfWide2x`: (string) Path to a 4640x1440 image
57
76
 
58
77
  _Warning_:
59
78
 
@@ -62,7 +81,7 @@ When this plugin is used to generate files in the iOS directory that build an Ap
62
81
  ```json
63
82
  {
64
83
  "dependencies": {
65
- "react-native": "npm:react-native-tvos@^0.72.6-1"
84
+ "react-native": "npm:react-native-tvos@^0.73.2-0"
66
85
  }
67
86
  }
68
87
  ```
@@ -71,6 +90,6 @@ If this is not the case, the plugin will run successfully, but Cocoapods install
71
90
 
72
91
  _Warning_:
73
92
 
74
- 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.
93
+ 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.
75
94
 
76
- 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.
95
+ 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
@@ -1,3 +1,29 @@
1
+ export type AppleTVImages = {
2
+ /**
3
+ * Path to 400x240 image
4
+ */
5
+ iconSmall: string;
6
+ /**
7
+ * Path to 1280x760 image
8
+ */
9
+ icon: string;
10
+ /**
11
+ * Path to 1920x720 image
12
+ */
13
+ topShelf: string;
14
+ /**
15
+ * Path to 3840x1440 image
16
+ */
17
+ topShelf2x: string;
18
+ /**
19
+ * Path to 2320x720 image
20
+ */
21
+ topShelfWide: string;
22
+ /**
23
+ * Path to 4640x1440 image
24
+ */
25
+ topShelfWide2x: string;
26
+ };
1
27
  export type ConfigData = {
2
28
  /**
3
29
  * If true, prebuild should generate Android and iOS files for TV (Android TV and Apple TV).
@@ -7,7 +33,12 @@ export type ConfigData = {
7
33
  */
8
34
  isTV?: boolean;
9
35
  /**
10
- * If true, verbose warnings will be shown during plugin execution. (Defaults to false.)
36
+ * Deprecated. Verbose logging is now shown as in other config plugins, by setting an environment variable:
37
+ * EXPO_DEBUG=1
38
+ * or
39
+ * DEBUG=expo:* (shows debug messages from all plugins)
40
+ * or
41
+ * DEBUG=expo:react-native-tvos:config-tv (shows debug messages from this plugin only)
11
42
  */
12
43
  showVerboseWarnings?: boolean;
13
44
  /**
@@ -18,4 +49,20 @@ export type ConfigData = {
18
49
  * If set, Android code that references Flipper will be removed. (Defaults to true.)
19
50
  */
20
51
  removeFlipperOnAndroid?: boolean;
52
+ /**
53
+ * If set, this should be a path to an existing PNG file appropriate for an Android TV banner image.
54
+ * See https://developer.android.com/design/ui/tv/guides/system/tv-app-icon-guidelines#banner
55
+ * The Android manifest will be modified to reference this image, and the image will be copied into
56
+ * Android resource drawable directories.
57
+ */
58
+ androidTVBanner?: string;
59
+ /**
60
+ * If set, this is an object with the paths to images needed to construct the Apple TV icon and
61
+ * top shelf brand assets. The images will be used to construct a brand asset catalog in the Xcode
62
+ * project Image catalog, and the project updated to use the brand assets as the source for the app
63
+ * icons. If this property is set, all six image paths must be defined and the files must exist,
64
+ * or an error will be thrown. The images need to be the exact sizes shown here, in order to avoid
65
+ * errors during Xcode compilation and on submission to the App Store or TestFlight.
66
+ */
67
+ appleTVImages?: AppleTVImages;
21
68
  };
@@ -0,0 +1,85 @@
1
+ export type ContentsJsonImageIdiom = 'tv';
2
+ export type ContentsJsonImageAppearance = {
3
+ appearance: 'luminosity';
4
+ value: 'dark';
5
+ };
6
+ export type ContentsJsonImageScale = '1x' | '2x' | '3x';
7
+ export type ContentsJsonImageRole = 'primary-app-icon' | 'top-shelf-image' | 'top-shelf-image-wide';
8
+ export interface ContentsJsonImage {
9
+ appearances?: ContentsJsonImageAppearance[];
10
+ idiom: ContentsJsonImageIdiom;
11
+ size?: string;
12
+ scale?: ContentsJsonImageScale;
13
+ filename: string;
14
+ }
15
+ export interface ContentsJsonImageLayer {
16
+ filename: string;
17
+ }
18
+ export interface ContentsJsonAsset {
19
+ filename?: string;
20
+ idiom?: ContentsJsonImageIdiom;
21
+ role?: ContentsJsonImageRole;
22
+ size?: string;
23
+ }
24
+ export interface ContentsJson {
25
+ assets?: ContentsJsonAsset[];
26
+ images?: ContentsJsonImage[];
27
+ layers?: ContentsJsonImageLayer[];
28
+ info: {
29
+ version: number;
30
+ author: string;
31
+ };
32
+ }
33
+ export interface SourceImageJson {
34
+ path: string;
35
+ scale?: string;
36
+ }
37
+ export interface SourceImageSetJson {
38
+ name: string;
39
+ sourceImages: SourceImageJson[];
40
+ }
41
+ export interface SourceImageLayerJson {
42
+ name: string;
43
+ sourceImages: SourceImageJson[];
44
+ }
45
+ export interface SourceImageStackJson {
46
+ name: string;
47
+ sourceLayers: SourceImageLayerJson[];
48
+ }
49
+ export interface SourceBrandAssetJson {
50
+ role: ContentsJsonImageRole;
51
+ size: string;
52
+ imageStack?: SourceImageStackJson;
53
+ imageSet?: SourceImageSetJson;
54
+ }
55
+ export interface SourceBrandAssetsJson {
56
+ name: string;
57
+ assets: SourceBrandAssetJson[];
58
+ }
59
+ /**
60
+ * Writes the Config.json which is used to assign images to their respective platform, dpi, and idiom.
61
+ *
62
+ * @param directory path to add the Contents.json to.
63
+ * @param contents image json data
64
+ */
65
+ export declare function writeContentsJsonAsync(directory: string, options: {
66
+ assets?: ContentsJsonAsset[];
67
+ images?: ContentsJsonImage[];
68
+ layers?: ContentsJsonImageLayer[];
69
+ }): Promise<void>;
70
+ /**
71
+ * Creates an image set directory with its Contents.json and any images
72
+ */
73
+ export declare function createImageSetAsync(destinationPath: string, imageSet: SourceImageSetJson): Promise<void>;
74
+ /**
75
+ * Creates an image stack layer directory with its Contents.json and any images
76
+ */
77
+ export declare function createImageStackLayerAsync(destinationPath: string, layer: SourceImageLayerJson): Promise<void>;
78
+ /**
79
+ * Creates an image stack directory with its Contents.json and any layers
80
+ */
81
+ export declare function createImageStackAsync(destinationPath: string, stack: SourceImageStackJson): Promise<void>;
82
+ /**
83
+ * Creates a brand assets directory with its Contents.json and any assets
84
+ */
85
+ export declare function createBrandAssetsAsync(destinationPath: string, brandAssets: SourceBrandAssetsJson): Promise<void>;
@@ -0,0 +1,113 @@
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.createBrandAssetsAsync = exports.createImageStackAsync = exports.createImageStackLayerAsync = exports.createImageSetAsync = exports.writeContentsJsonAsync = void 0;
7
+ const fs_1 = require("fs");
8
+ const path_1 = __importDefault(require("path"));
9
+ /**
10
+ * Writes the Config.json which is used to assign images to their respective platform, dpi, and idiom.
11
+ *
12
+ * @param directory path to add the Contents.json to.
13
+ * @param contents image json data
14
+ */
15
+ async function writeContentsJsonAsync(directory, options) {
16
+ await fs_1.promises.mkdir(directory, { recursive: true });
17
+ await fs_1.promises.writeFile(path_1.default.join(directory, 'Contents.json'), JSON.stringify({
18
+ assets: options.assets,
19
+ images: options.images,
20
+ layers: options.layers,
21
+ info: {
22
+ version: 1,
23
+ // common practice is for the tool that generated the icons to be the "author"
24
+ author: 'expo',
25
+ },
26
+ }, null, 2));
27
+ }
28
+ exports.writeContentsJsonAsync = writeContentsJsonAsync;
29
+ /**
30
+ * Creates an image set directory with its Contents.json and any images
31
+ */
32
+ async function createImageSetAsync(destinationPath, imageSet) {
33
+ const imageSetPath = path_1.default.join(destinationPath, `${imageSet.name}.imageset`);
34
+ await writeContentsJsonAsync(imageSetPath, {
35
+ images: imageSet.sourceImages.map((image) => ({
36
+ filename: path_1.default.basename(image.path),
37
+ idiom: 'tv',
38
+ scale: image.scale,
39
+ })),
40
+ });
41
+ for (const image of imageSet.sourceImages) {
42
+ await fs_1.promises.copyFile(image.path, path_1.default.join(imageSetPath, path_1.default.basename(image.path)));
43
+ }
44
+ }
45
+ exports.createImageSetAsync = createImageSetAsync;
46
+ /**
47
+ * Creates an image stack layer directory with its Contents.json and any images
48
+ */
49
+ async function createImageStackLayerAsync(destinationPath, layer) {
50
+ const imageStackLayerPath = path_1.default.join(destinationPath, `${layer.name}.imagestacklayer`);
51
+ await writeContentsJsonAsync(imageStackLayerPath, {});
52
+ await createImageSetAsync(imageStackLayerPath, {
53
+ name: 'Content',
54
+ sourceImages: layer.sourceImages,
55
+ });
56
+ }
57
+ exports.createImageStackLayerAsync = createImageStackLayerAsync;
58
+ /**
59
+ * Creates an image stack directory with its Contents.json and any layers
60
+ */
61
+ async function createImageStackAsync(destinationPath, stack) {
62
+ const imageStackPath = path_1.default.join(destinationPath, `${stack.name}.imagestack`);
63
+ await writeContentsJsonAsync(imageStackPath, {
64
+ layers: stack.sourceLayers.map((layer) => ({
65
+ filename: `${layer.name}.imagestacklayer`,
66
+ })),
67
+ });
68
+ for (const sourceLayer of stack.sourceLayers) {
69
+ await createImageStackLayerAsync(imageStackPath, {
70
+ name: sourceLayer.name,
71
+ sourceImages: sourceLayer.sourceImages,
72
+ });
73
+ }
74
+ }
75
+ exports.createImageStackAsync = createImageStackAsync;
76
+ /**
77
+ * Creates a brand assets directory with its Contents.json and any assets
78
+ */
79
+ async function createBrandAssetsAsync(destinationPath, brandAssets) {
80
+ const brandAssetsPath = path_1.default.join(destinationPath, `${brandAssets.name}.brandassets`);
81
+ await writeContentsJsonAsync(brandAssetsPath, {
82
+ assets: brandAssets.assets.map((brandAsset) => {
83
+ if (brandAsset.imageStack) {
84
+ return {
85
+ filename: `${brandAsset.imageStack.name}.imagestack`,
86
+ role: brandAsset.role,
87
+ size: brandAsset.size,
88
+ idiom: 'tv',
89
+ };
90
+ }
91
+ else if (brandAsset.imageSet) {
92
+ return {
93
+ filename: `${brandAsset.imageSet.name}.imageset`,
94
+ role: brandAsset.role,
95
+ size: brandAsset.size,
96
+ idiom: 'tv',
97
+ };
98
+ }
99
+ else {
100
+ return {}; // Should never happen, but need this to keep Typescript happy
101
+ }
102
+ }),
103
+ });
104
+ for (const asset of brandAssets.assets) {
105
+ if (asset.imageSet) {
106
+ await createImageSetAsync(brandAssetsPath, asset.imageSet);
107
+ }
108
+ if (asset.imageStack) {
109
+ await createImageStackAsync(brandAssetsPath, asset.imageStack);
110
+ }
111
+ }
112
+ }
113
+ exports.createBrandAssetsAsync = createBrandAssetsAsync;
@@ -0,0 +1,8 @@
1
+ import { ConfigData } from '../types';
2
+ export declare const packageNameAndVersion: string;
3
+ export declare function isTVEnabled(params: ConfigData): boolean;
4
+ export declare function tvosDeploymentTarget(params: ConfigData): string;
5
+ export declare function shouldRemoveFlipperOnAndroid(params: ConfigData): boolean;
6
+ export declare function androidTVBanner(params: ConfigData): string | undefined;
7
+ export declare const appleTVImageTypes: string[];
8
+ export declare function appleTVImagePathForType(params: ConfigData, imageType: string): string | undefined;
@@ -0,0 +1,57 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.appleTVImagePathForType = exports.appleTVImageTypes = exports.androidTVBanner = exports.shouldRemoveFlipperOnAndroid = exports.tvosDeploymentTarget = exports.isTVEnabled = exports.packageNameAndVersion = void 0;
4
+ const getenv_1 = require("getenv");
5
+ class Env {
6
+ /** Enable prebuild for TV */
7
+ get EXPO_TV() {
8
+ return (0, getenv_1.boolish)('EXPO_TV', false);
9
+ }
10
+ }
11
+ const env = new Env();
12
+ const pkg = require('../../package.json');
13
+ const defaultTvosDeploymentVersion = '13.4';
14
+ exports.packageNameAndVersion = `${pkg.name}@${pkg.version}`;
15
+ function isTVEnabled(params) {
16
+ return env.EXPO_TV || (params?.isTV ?? false);
17
+ }
18
+ exports.isTVEnabled = isTVEnabled;
19
+ function tvosDeploymentTarget(params) {
20
+ return params?.tvosDeploymentTarget ?? defaultTvosDeploymentVersion;
21
+ }
22
+ exports.tvosDeploymentTarget = tvosDeploymentTarget;
23
+ function shouldRemoveFlipperOnAndroid(params) {
24
+ return params?.removeFlipperOnAndroid ?? true;
25
+ }
26
+ exports.shouldRemoveFlipperOnAndroid = shouldRemoveFlipperOnAndroid;
27
+ function androidTVBanner(params) {
28
+ return params?.androidTVBanner;
29
+ }
30
+ exports.androidTVBanner = androidTVBanner;
31
+ exports.appleTVImageTypes = [
32
+ 'icon',
33
+ 'iconSmall',
34
+ 'topShelf',
35
+ 'topShelf2x',
36
+ 'topShelfWide',
37
+ 'topShelfWide2x',
38
+ ];
39
+ function appleTVImagePathForType(params, imageType) {
40
+ switch (imageType) {
41
+ case 'icon':
42
+ return params?.appleTVImages?.icon;
43
+ case 'iconSmall':
44
+ return params?.appleTVImages?.iconSmall;
45
+ case 'topShelf':
46
+ return params?.appleTVImages?.topShelf;
47
+ case 'topShelf2x':
48
+ return params?.appleTVImages?.topShelf2x;
49
+ case 'topShelfWide':
50
+ return params?.appleTVImages?.topShelfWide;
51
+ case 'topShelfWide2x':
52
+ return params?.appleTVImages?.topShelfWide2x;
53
+ default:
54
+ return undefined;
55
+ }
56
+ }
57
+ exports.appleTVImagePathForType = appleTVImagePathForType;
@@ -0,0 +1,3 @@
1
+ export * from './appleBrandAssets';
2
+ export * from './config';
3
+ export * from './log';
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./appleBrandAssets"), exports);
18
+ __exportStar(require("./config"), exports);
19
+ __exportStar(require("./log"), exports);
@@ -0,0 +1,6 @@
1
+ import { ConfigData } from '../types';
2
+ export declare function verboseLog(message: string, options?: {
3
+ params?: ConfigData;
4
+ platform?: 'android' | 'ios';
5
+ property?: string;
6
+ }): void;
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.verboseLog = void 0;
4
+ const debug = require('debug')('expo:react-native-tvos:config-tv');
5
+ function verboseLog(message, options) {
6
+ const tokens = [message];
7
+ options?.property && tokens.unshift(options?.property);
8
+ options?.platform && tokens.unshift(options?.platform);
9
+ debug(tokens.join(': '));
10
+ }
11
+ exports.verboseLog = verboseLog;
package/build/withTV.js CHANGED
@@ -2,14 +2,28 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const config_plugins_1 = require("expo/config-plugins");
4
4
  const withTVAndroidManifest_1 = require("./withTVAndroidManifest");
5
+ const withTVAppleIconImages_1 = require("./withTVAppleIconImages");
5
6
  const withTVPodfile_1 = require("./withTVPodfile");
6
7
  const withTVSplashScreen_1 = require("./withTVSplashScreen");
7
8
  const withTVXcodeProject_1 = require("./withTVXcodeProject");
8
9
  const withTVAndroidRemoveFlipper_1 = require("./withTVAndroidRemoveFlipper");
10
+ const withTVAndroidBannerImage_1 = require("./withTVAndroidBannerImage");
11
+ const utils_1 = require("./utils");
12
+ const withTVNoEffect = (config, params = {}) => {
13
+ (0, utils_1.verboseLog)(`${utils_1.packageNameAndVersion}: isTV == false, plugin will have no effect`, {});
14
+ return config;
15
+ };
9
16
  const withTVPlugin = (config, params = {}) => {
17
+ const isTV = (0, utils_1.isTVEnabled)(params);
18
+ if (!isTV) {
19
+ config = withTVNoEffect(config, params);
20
+ return config;
21
+ }
22
+ config = (0, withTVAppleIconImages_1.withTVAppleIconImages)(config, params); // This should be done before Apple Xcode project config
10
23
  config = (0, withTVXcodeProject_1.withTVXcodeProject)(config, params);
11
24
  config = (0, withTVPodfile_1.withTVPodfile)(config, params);
12
25
  config = (0, withTVSplashScreen_1.withTVSplashScreen)(config, params);
26
+ config = (0, withTVAndroidBannerImage_1.withTVAndroidBannerImage)(config, params); // This should be done before Android manifest config
13
27
  config = (0, withTVAndroidManifest_1.withTVAndroidManifest)(config, params);
14
28
  config = (0, withTVAndroidRemoveFlipper_1.withTVAndroidRemoveFlipper)(config, params);
15
29
  return config;
@@ -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;
@@ -9,30 +9,31 @@ const fs_1 = require("fs");
9
9
  const glob_1 = __importDefault(require("glob"));
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 withTVAndroidRemoveFlipper = (c, params = {}) => {
15
- const isTV = (0, utils_1.isTVEnabled)(params);
16
- const verbose = (0, utils_1.showVerboseWarnings)(params);
17
14
  const androidRemoveFlipper = (0, utils_1.shouldRemoveFlipperOnAndroid)(params);
18
15
  return (0, config_plugins_1.withDangerousMod)(c, [
19
16
  'android',
20
17
  // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
21
18
  async (config) => {
22
- if (isTV && androidRemoveFlipper) {
19
+ if (androidRemoveFlipper) {
23
20
  // Modify main application
24
21
  const mainApplicationFile = mainApplicationFilePath(config.modRequest.platformProjectRoot);
25
- if (verbose) {
26
- config_plugins_1.WarningAggregator.addWarningAndroid('source', `${pkg.name}@${pkg.version}: removing Flipper from MainApplication file`);
27
- }
22
+ (0, utils_1.verboseLog)('removing Flipper from MainApplication file', {
23
+ params,
24
+ platform: 'android',
25
+ property: 'manifest',
26
+ });
28
27
  const mainApplicationContents = await fs_1.promises.readFile(mainApplicationFile, 'utf8');
29
28
  const mainApplicationModifiedContents = commentOutLinesWithString(mainApplicationContents, 'Flipper');
30
29
  await fs_1.promises.writeFile(mainApplicationFile, mainApplicationModifiedContents, 'utf-8');
31
30
  // Modify app/build.gradle
32
31
  const buildGradleFile = appBuildGradleFilePath(config.modRequest.platformProjectRoot);
33
- if (verbose) {
34
- config_plugins_1.WarningAggregator.addWarningAndroid('source', `${pkg.name}@${pkg.version}: removing Flipper from android/app/build.gradle`);
35
- }
32
+ (0, utils_1.verboseLog)('removing Flipper from android/app/build.gradle', {
33
+ params,
34
+ platform: 'android',
35
+ property: 'manifest',
36
+ });
36
37
  const buildGradleContents = await fs_1.promises.readFile(buildGradleFile, 'utf-8');
37
38
  const buildGradleModifiedContents = commentOutLinesWithString(buildGradleContents, 'flipper');
38
39
  await fs_1.promises.writeFile(buildGradleFile, buildGradleModifiedContents, 'utf-8');
@@ -0,0 +1,7 @@
1
+ import { ConfigPlugin } from 'expo/config-plugins';
2
+ import { ConfigData } from './types';
3
+ /**
4
+ * Constructs Apple TV brand assets from images passed into the `appleTVImages` plugin property
5
+ * If any images do not exist, an exception is thrown.
6
+ */
7
+ export declare const withTVAppleIconImages: ConfigPlugin<ConfigData>;
@@ -0,0 +1,148 @@
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.withTVAppleIconImages = 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 { getProjectName } = config_plugins_1.IOSConfig.XcodeUtils;
12
+ /**
13
+ * Constructs Apple TV brand assets from images passed into the `appleTVImages` plugin property
14
+ * If any images do not exist, an exception is thrown.
15
+ */
16
+ const withTVAppleIconImages = (c, params = {}) => {
17
+ return (0, config_plugins_1.withDangerousMod)(c, [
18
+ 'ios',
19
+ // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
20
+ async (config) => {
21
+ if (!params.appleTVImages) {
22
+ return config;
23
+ }
24
+ (0, utils_1.verboseLog)(`adding Apple TV brand assets to Apple TV native code`, {
25
+ params,
26
+ platform: 'ios',
27
+ property: 'xcodeproject',
28
+ });
29
+ utils_1.appleTVImageTypes.forEach((imageType) => {
30
+ const imagePath = (0, utils_1.appleTVImagePathForType)(params, imageType);
31
+ if (!imagePath) {
32
+ throw new Error(`One or more image paths not defined`);
33
+ }
34
+ if (!(0, fs_1.existsSync)(imagePath)) {
35
+ throw new Error(`No image found at path ${imagePath}`);
36
+ }
37
+ });
38
+ const projectRoot = config.modRequest.projectRoot;
39
+ const iosImagesPath = path_1.default.join(getIosNamedProjectPath(projectRoot), IMAGES_PATH);
40
+ const iconSourceImages = [
41
+ {
42
+ path: params.appleTVImages.iconSmall,
43
+ scale: '1x',
44
+ },
45
+ {
46
+ path: params.appleTVImages.icon,
47
+ scale: '2x',
48
+ },
49
+ ];
50
+ const appStoreIconSourceImages = [
51
+ {
52
+ path: params.appleTVImages.icon,
53
+ },
54
+ ];
55
+ const topShelfSourceImages = [
56
+ {
57
+ path: params.appleTVImages.topShelf,
58
+ scale: '1x',
59
+ },
60
+ {
61
+ path: params.appleTVImages.topShelf2x,
62
+ scale: '2x',
63
+ },
64
+ ];
65
+ const topShelfWideSourceImages = [
66
+ {
67
+ path: params.appleTVImages.topShelfWide,
68
+ scale: '1x',
69
+ },
70
+ {
71
+ path: params.appleTVImages.topShelfWide2x,
72
+ scale: '2x',
73
+ },
74
+ ];
75
+ const sourceBrandAssets = {
76
+ name: 'TVAppIcon',
77
+ assets: [
78
+ {
79
+ role: 'top-shelf-image',
80
+ size: '1920x720',
81
+ imageSet: {
82
+ name: 'Top Shelf Image',
83
+ sourceImages: topShelfSourceImages,
84
+ },
85
+ },
86
+ {
87
+ role: 'top-shelf-image-wide',
88
+ size: '2320x720',
89
+ imageSet: {
90
+ name: 'Top Shelf Image Wide',
91
+ sourceImages: topShelfWideSourceImages,
92
+ },
93
+ },
94
+ {
95
+ role: 'primary-app-icon',
96
+ size: '1280x768',
97
+ imageStack: {
98
+ name: 'App Icon - App Store',
99
+ sourceLayers: [
100
+ {
101
+ name: 'Front',
102
+ sourceImages: appStoreIconSourceImages,
103
+ },
104
+ {
105
+ name: 'Middle',
106
+ sourceImages: appStoreIconSourceImages,
107
+ },
108
+ {
109
+ name: 'Back',
110
+ sourceImages: appStoreIconSourceImages,
111
+ },
112
+ ],
113
+ },
114
+ },
115
+ {
116
+ role: 'primary-app-icon',
117
+ size: '400x240',
118
+ imageStack: {
119
+ name: 'App Icon',
120
+ sourceLayers: [
121
+ {
122
+ name: 'Front',
123
+ sourceImages: iconSourceImages,
124
+ },
125
+ {
126
+ name: 'Middle',
127
+ sourceImages: iconSourceImages,
128
+ },
129
+ {
130
+ name: 'Back',
131
+ sourceImages: iconSourceImages,
132
+ },
133
+ ],
134
+ },
135
+ },
136
+ ],
137
+ };
138
+ await (0, utils_1.createBrandAssetsAsync)(iosImagesPath, sourceBrandAssets);
139
+ return config;
140
+ },
141
+ ]);
142
+ };
143
+ exports.withTVAppleIconImages = withTVAppleIconImages;
144
+ function getIosNamedProjectPath(projectRoot) {
145
+ const projectName = getProjectName(projectRoot);
146
+ return path_1.default.join(projectRoot, 'ios', projectName);
147
+ }
148
+ const IMAGES_PATH = 'Images.xcassets';
@@ -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,50 +1,44 @@
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';
37
34
  if (typeof buildSettings?.IOS_DEPLOYMENT_TARGET !== 'undefined') {
38
35
  delete buildSettings?.IOS_DEPLOYMENT_TARGET;
39
36
  }
40
- }
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;
37
+ if (params.appleTVImages) {
38
+ // set the app icon source
39
+ if (buildSettings.ASSETCATALOG_COMPILER_APPICON_NAME === 'AppIcon') {
40
+ buildSettings.ASSETCATALOG_COMPILER_APPICON_NAME = 'TVAppIcon';
41
+ }
48
42
  }
49
43
  }
50
44
  }
@@ -52,39 +46,3 @@ function setXcodeProjectBuildSettings(config, { project, isTV, verbose, deployme
52
46
  return project;
53
47
  }
54
48
  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.4",
3
+ "version": "0.0.6",
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,12 +34,13 @@
34
34
  "react-native-tvos"
35
35
  ],
36
36
  "peerDependencies": {
37
- "expo": "^50.0.0-alpha.7"
37
+ "expo": "^50.0.6"
38
38
  },
39
39
  "devDependencies": {
40
40
  "@types/getenv": "^1.0.1",
41
41
  "expo-module-scripts": "^3.0.3",
42
- "jest": "^29.7.0"
42
+ "jest": "^29.7.0",
43
+ "memfs": "^4.7.6"
43
44
  },
44
45
  "upstreamPackage": "tv",
45
46
  "dependencies": {
package/build/utils.d.ts DELETED
@@ -1,5 +0,0 @@
1
- import { ConfigData } from './types';
2
- export declare function isTVEnabled(params: ConfigData): boolean;
3
- export declare function showVerboseWarnings(params: ConfigData): boolean;
4
- export declare function tvosDeploymentTarget(params: ConfigData): string;
5
- export declare function shouldRemoveFlipperOnAndroid(params: ConfigData): boolean;
package/build/utils.js DELETED
@@ -1,28 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.shouldRemoveFlipperOnAndroid = exports.tvosDeploymentTarget = exports.showVerboseWarnings = exports.isTVEnabled = void 0;
4
- const getenv_1 = require("getenv");
5
- class Env {
6
- /** Enable prebuild for TV */
7
- get EXPO_TV() {
8
- return (0, getenv_1.boolish)('EXPO_TV', false);
9
- }
10
- }
11
- const env = new Env();
12
- const defaultTvosDeploymentVersion = '13.4';
13
- function isTVEnabled(params) {
14
- return env.EXPO_TV || (params?.isTV ?? false);
15
- }
16
- exports.isTVEnabled = isTVEnabled;
17
- function showVerboseWarnings(params) {
18
- return params?.showVerboseWarnings ?? false;
19
- }
20
- exports.showVerboseWarnings = showVerboseWarnings;
21
- function tvosDeploymentTarget(params) {
22
- return params?.tvosDeploymentTarget ?? defaultTvosDeploymentVersion;
23
- }
24
- exports.tvosDeploymentTarget = tvosDeploymentTarget;
25
- function shouldRemoveFlipperOnAndroid(params) {
26
- return params?.removeFlipperOnAndroid ?? true;
27
- }
28
- exports.shouldRemoveFlipperOnAndroid = shouldRemoveFlipperOnAndroid;