From 5c5377c9902e06244b35d9d84f32856923a6d160 Mon Sep 17 00:00:00 2001 From: Tommy Nguyen <4123478+tido64@users.noreply.github.com> Date: Wed, 19 Mar 2025 15:44:01 +0100 Subject: [PATCH] refactor(apple): port `info_plist.rb` to JS --- ios/entitlements.mjs | 4 +- ios/infoPlist.mjs | 98 ++++++ ios/privacyManifest.mjs | 4 +- ios/utils.mjs | 52 ++- test/ios/entitlements.test.ts | 2 +- test/ios/infoPlist.test.ts | 619 ++++++++++++++++++++++++++++++++++ test/pack.test.ts | 1 + 7 files changed, 768 insertions(+), 12 deletions(-) create mode 100644 ios/infoPlist.mjs create mode 100644 test/ios/infoPlist.test.ts diff --git a/ios/entitlements.mjs b/ios/entitlements.mjs index 8f43d2cb1..e9d11ea72 100644 --- a/ios/entitlements.mjs +++ b/ios/entitlements.mjs @@ -1,7 +1,7 @@ // @ts-check import * as nodefs from "node:fs"; import * as path from "node:path"; -import { isObject, toPlist } from "./utils.mjs"; +import { isObject, plistFromJSON } from "./utils.mjs"; /** @import { ApplePlatform, JSONObject } from "../scripts/types.js"; */ @@ -56,7 +56,7 @@ export function generateEntitlements( } const filename = "App.entitlements"; - const entitlements = toPlist( + const entitlements = plistFromJSON( { ...(targetPlatform === "macos" ? DEFAULT_MACOS_ENTITLEMENTS diff --git a/ios/infoPlist.mjs b/ios/infoPlist.mjs new file mode 100644 index 000000000..5387bcf9b --- /dev/null +++ b/ios/infoPlist.mjs @@ -0,0 +1,98 @@ +// @ts-check +import * as nodefs from "node:fs"; +import * as path from "node:path"; +import { + isObject, + jsonFromPlist, + plistFromJSON, + projectPath, + resolveResources, +} from "./utils.mjs"; + +/** @import { ApplePlatform, JSONArray, JSONObject } from "../scripts/types.js"; */ + +/** + * @param {JSONObject} appConfig + * @param {ApplePlatform} targetPlatform + * @param {JSONObject} info + */ +function setMacProperties(appConfig, targetPlatform, info) { + if (targetPlatform !== "macos") { + return; + } + + const config = appConfig[targetPlatform]; + if (!isObject(config)) { + return; + } + + const category = config["applicationCategoryType"]; + if (typeof category === "string") { + info["LSApplicationCategoryType"] = category; + } + + const copyright = config["humanReadableCopyright"]; + if (typeof copyright === "string") { + info["NSHumanReadableCopyright"] = copyright; + } +} + +/** + * @param {JSONObject} appConfig + * @param {ApplePlatform} targetPlatform + * @param {string} destination + * @returns {void} + */ +export function generateInfoPlist( + appConfig, + targetPlatform, + destination, + fs = nodefs +) { + const filename = "ReactTestApp/Info.plist"; + const infoPlistSrc = projectPath(filename, targetPlatform); + + const info = jsonFromPlist(infoPlistSrc); + + const resources = resolveResources(appConfig, targetPlatform); + registerFonts(resources, targetPlatform, info); + setMacProperties(appConfig, targetPlatform, info); + + const infoPlistDst = path.join(destination, path.basename(infoPlistSrc)); + fs.writeFileSync(infoPlistDst, plistFromJSON(info, filename)); +} + +/** + * @param {JSONArray | undefined} resources + * @param {ApplePlatform} targetPlatform + * @param {JSONObject} info + */ +export function registerFonts(resources, targetPlatform, info) { + if (!resources) { + return; + } + + const fontFiles = [".otf", ".ttf"]; + const fonts = []; + for (const filename of resources) { + if (typeof filename === "string") { + const extname = path.extname(filename); + if (fontFiles.includes(extname)) { + fonts.push(path.basename(filename)); + } + } + } + + if (fonts.length > 0) { + switch (targetPlatform) { + case "macos": + // https://developer.apple.com/documentation/bundleresources/information_property_list/atsapplicationfontspath + info["ATSApplicationFontsPath"] = "."; + break; + default: + // https://developer.apple.com/documentation/uikit/text_display_and_fonts/adding_a_custom_font_to_your_app + info["UIAppFonts"] = fonts; + break; + } + } +} diff --git a/ios/privacyManifest.mjs b/ios/privacyManifest.mjs index 04e9d9441..da236b607 100644 --- a/ios/privacyManifest.mjs +++ b/ios/privacyManifest.mjs @@ -1,7 +1,7 @@ // @ts-check import * as nodefs from "node:fs"; import * as path from "node:path"; -import { isObject, toPlist } from "./utils.mjs"; +import { isObject, plistFromJSON } from "./utils.mjs"; /** * @import { ApplePlatform, JSONObject, JSONValue } from "../scripts/types.js"; @@ -107,6 +107,6 @@ export function generatePrivacyManifest( } const filename = "PrivacyInfo.xcprivacy"; - const xcprivacy = toPlist(manifest, filename); + const xcprivacy = plistFromJSON(manifest, filename); fs.writeFileSync(path.join(destination, filename), xcprivacy); } diff --git a/ios/utils.mjs b/ios/utils.mjs index d6be217f1..fcff70b38 100644 --- a/ios/utils.mjs +++ b/ios/utils.mjs @@ -18,13 +18,20 @@ export function isObject(obj) { } /** - * @param {string} p - * @param {ApplePlatform} targetPlatform - * @returns {string} + * @param {string} filename + * @returns {JSONObject} */ -export function projectPath(p, targetPlatform) { - const packageDir = path.dirname(path.dirname(fileURLToPath(import.meta.url))); - return path.join(packageDir, targetPlatform, p); +export function jsonFromPlist(filename) { + const args = ["-convert", "json", "-o", "-", filename]; + const plutil = spawnSync("/usr/bin/plutil", args, { + stdio: ["ignore", "pipe", "inherit"], + }); + + if (plutil.status !== 0) { + throw new Error(`Failed to read '${filename}'`); + } + + return JSON.parse(plutil.stdout.toString()); } /** @@ -32,7 +39,7 @@ export function projectPath(p, targetPlatform) { * @param {string} filename * @returns {string} */ -export function toPlist(source, filename) { +export function plistFromJSON(source, filename) { const args = ["-convert", "xml1", "-r", "-o", "-", "--", "-"]; const plutil = spawnSync("/usr/bin/plutil", args, { stdio: ["pipe", "pipe", "inherit"], @@ -45,3 +52,34 @@ export function toPlist(source, filename) { return plutil.stdout.toString(); } + +/** + * @param {string} p + * @param {ApplePlatform} targetPlatform + * @returns {string} + */ +export function projectPath(p, targetPlatform) { + const packageDir = path.dirname(path.dirname(fileURLToPath(import.meta.url))); + return path.join(packageDir, targetPlatform, p); +} + +/** + * @param {JSONObject} appConfig + * @param {ApplePlatform} targetPlatform + * @returns {JSONValue[] | undefined} + */ +export function resolveResources(appConfig, targetPlatform) { + const resources = appConfig["resources"]; + if (resources && typeof resources === "object") { + if (Array.isArray(resources)) { + return resources; + } + + const res = resources[targetPlatform]; + if (Array.isArray(res)) { + return res; + } + } + + return undefined; +} diff --git a/test/ios/entitlements.test.ts b/test/ios/entitlements.test.ts index 99ef5c10c..a8585a790 100644 --- a/test/ios/entitlements.test.ts +++ b/test/ios/entitlements.test.ts @@ -6,7 +6,7 @@ import { fs, setMockFiles } from "../fs.mock.ts"; const macosOnly = { skip: process.platform === "win32" }; -describe("generatePrivacyManifest()", macosOnly, () => { +describe("generateEntitlements()", macosOnly, () => { const targetPlatforms = ["ios", "macos", "visionos"] as const; function generateEntitlements( diff --git a/test/ios/infoPlist.test.ts b/test/ios/infoPlist.test.ts new file mode 100644 index 000000000..b7d62f29f --- /dev/null +++ b/test/ios/infoPlist.test.ts @@ -0,0 +1,619 @@ +import { deepEqual } from "node:assert/strict"; +import { afterEach, describe, it } from "node:test"; +import { generateInfoPlist as generateInfoPlistActual } from "../../ios/infoPlist.mjs"; +import type { JSONObject } from "../../scripts/types.ts"; +import { fs, setMockFiles } from "../fs.mock.ts"; + +const macosOnly = { skip: process.platform === "win32" }; + +describe("generateInfoPlist()", macosOnly, () => { + const targetPlatforms = ["ios", "macos", "visionos"] as const; + + function generateInfoPlist( + config: JSONObject, + platform: (typeof targetPlatforms)[number] + ): void { + const destination = "."; + fs.mkdirSync(destination, { recursive: true, mode: 0o755 }); + generateInfoPlistActual(config, platform, destination, fs); + } + + function readInfoPlist() { + return fs.readFileSync("Info.plist", { encoding: "utf-8" }).split("\n"); + } + + afterEach(() => { + setMockFiles(); + }); + + for (const platform of targetPlatforms) { + it(`[${platform}] generates a default 'Info.plist'`, () => { + generateInfoPlist({}, platform); + + deepEqual(readInfoPlist(), DEFAULT_INFO_PLIST[platform]); + }); + + it(`[${platform}] registers font files`, () => { + generateInfoPlist( + { + resources: ["OpenType.otf", "TrueType.ttf"], + }, + platform + ); + + deepEqual(readInfoPlist(), INFO_PLIST_WITH_FONTS[platform]); + }); + } + + it("[macos] sets application category type", () => { + generateInfoPlist( + { macos: { applicationCategoryType: "public.app-category.games" } }, + "macos" + ); + + deepEqual(readInfoPlist(), INFO_PLIST_MACOS.applicationCategoryType); + }); + + it("[macos] sets human-readable copyright", () => { + generateInfoPlist( + { + macos: { + humanReadableCopyright: + "Copyright © 2025 Contoso. All rights reserved.", + }, + }, + "macos" + ); + + deepEqual(readInfoPlist(), INFO_PLIST_MACOS.humanReadableCopyright); + }); +}); + +const DEFAULT_INFO_PLIST = { + ios: [ + '', + '', + '', + "", + " CFBundleDevelopmentRegion", + " $(DEVELOPMENT_LANGUAGE)", + " CFBundleDisplayName", + " $(PRODUCT_DISPLAY_NAME)", + " CFBundleExecutable", + " $(EXECUTABLE_NAME)", + " CFBundleIdentifier", + " $(PRODUCT_BUNDLE_IDENTIFIER)", + " CFBundleInfoDictionaryVersion", + " 6.0", + " CFBundleName", + " $(PRODUCT_NAME)", + " CFBundlePackageType", + " $(PRODUCT_BUNDLE_PACKAGE_TYPE)", + " CFBundleShortVersionString", + " $(PRODUCT_VERSION)", + " CFBundleURLTypes", + " ", + " ", + " CFBundleURLSchemes", + " ", + " $(PRODUCT_BUNDLE_IDENTIFIER)", + " msauth.$(PRODUCT_BUNDLE_IDENTIFIER)", + " ", + " ", + " ", + " CFBundleVersion", + " $(PRODUCT_BUILD_NUMBER)", + " LSApplicationQueriesSchemes", + " ", + " linkedin", + " ms-outlook", + " ms-sfb", + " msauth", + " msauthv2", + " msauthv3", + " ", + " LSRequiresIPhoneOS", + " ", + " NSAppTransportSecurity", + " ", + " NSAllowsLocalNetworking", + " ", + " ", + " NSCameraUsageDescription", + " The camera is needed to scan QR codes for JS bundle URLs", + " UIApplicationSceneManifest", + " ", + " UIApplicationSupportsMultipleScenes", + " ", + " UISceneConfigurations", + " ", + " UIWindowSceneSessionRoleApplication", + " ", + " ", + " UISceneConfigurationName", + " Default Configuration", + " UISceneDelegateClassName", + " $(PRODUCT_MODULE_NAME).SceneDelegate", + " ", + " ", + " ", + " ", + " UILaunchScreen", + " ", + " UIRequiredDeviceCapabilities", + " ", + " arm64", + " ", + " UISupportedInterfaceOrientations", + " ", + " UIInterfaceOrientationPortrait", + " UIInterfaceOrientationPortraitUpsideDown", + " UIInterfaceOrientationLandscapeLeft", + " UIInterfaceOrientationLandscapeRight", + " ", + " UIViewControllerBasedStatusBarAppearance", + " ", + "", + "", + "", + ], + macos: [ + '', + '', + '', + "", + " CFBundleDevelopmentRegion", + " $(DEVELOPMENT_LANGUAGE)", + " CFBundleDisplayName", + " $(PRODUCT_DISPLAY_NAME)", + " CFBundleExecutable", + " $(EXECUTABLE_NAME)", + " CFBundleIdentifier", + " $(PRODUCT_BUNDLE_IDENTIFIER)", + " CFBundleInfoDictionaryVersion", + " 6.0", + " CFBundleName", + " $(PRODUCT_DISPLAY_NAME)", + " CFBundlePackageType", + " $(PRODUCT_BUNDLE_PACKAGE_TYPE)", + " CFBundleShortVersionString", + " $(PRODUCT_VERSION)", + " CFBundleVersion", + " $(PRODUCT_BUILD_NUMBER)", + " LSApplicationCategoryType", + " public.app-category.developer-tools", + " LSMinimumSystemVersion", + " $(MACOSX_DEPLOYMENT_TARGET)", + " NSAppTransportSecurity", + " ", + " NSAllowsLocalNetworking", + " ", + " ", + " NSHumanReadableCopyright", + " Copyright © 2020 Microsoft. All rights reserved.", + " NSMainStoryboardFile", + " Main", + " NSPrincipalClass", + " NSApplication", + " NSSupportsAutomaticTermination", + " ", + " NSSupportsSuddenTermination", + " ", + "", + "", + "", + ], + visionos: [ + '', + '', + '', + "", + " CFBundleDevelopmentRegion", + " $(DEVELOPMENT_LANGUAGE)", + " CFBundleDisplayName", + " $(PRODUCT_DISPLAY_NAME)", + " CFBundleExecutable", + " $(EXECUTABLE_NAME)", + " CFBundleIdentifier", + " $(PRODUCT_BUNDLE_IDENTIFIER)", + " CFBundleInfoDictionaryVersion", + " 6.0", + " CFBundleName", + " $(PRODUCT_NAME)", + " CFBundlePackageType", + " $(PRODUCT_BUNDLE_PACKAGE_TYPE)", + " CFBundleShortVersionString", + " $(PRODUCT_VERSION)", + " CFBundleURLTypes", + " ", + " ", + " CFBundleURLSchemes", + " ", + " $(PRODUCT_BUNDLE_IDENTIFIER)", + " msauth.$(PRODUCT_BUNDLE_IDENTIFIER)", + " ", + " ", + " ", + " CFBundleVersion", + " $(PRODUCT_BUILD_NUMBER)", + " LSApplicationQueriesSchemes", + " ", + " linkedin", + " ms-outlook", + " ms-sfb", + " msauth", + " msauthv2", + " msauthv3", + " ", + " LSRequiresIPhoneOS", + " ", + " NSAppTransportSecurity", + " ", + " NSAllowsLocalNetworking", + " ", + " ", + " UIApplicationSceneManifest", + " ", + " UIApplicationSupportsMultipleScenes", + " ", + " UISceneConfigurations", + " ", + " UIWindowSceneSessionRoleApplication", + " ", + " ", + " UISceneConfigurationName", + " Default Configuration", + " UISceneDelegateClassName", + " $(PRODUCT_MODULE_NAME).SceneDelegate", + " ", + " ", + " ", + " ", + " UILaunchScreen", + " ", + " UIRequiredDeviceCapabilities", + " ", + " arm64", + " ", + " UISupportedInterfaceOrientations", + " ", + " UIInterfaceOrientationPortrait", + " UIInterfaceOrientationPortraitUpsideDown", + " UIInterfaceOrientationLandscapeLeft", + " UIInterfaceOrientationLandscapeRight", + " ", + " UIViewControllerBasedStatusBarAppearance", + " ", + "", + "", + "", + ], +}; + +const INFO_PLIST_WITH_FONTS = { + ios: [ + '', + '', + '', + "", + " CFBundleDevelopmentRegion", + " $(DEVELOPMENT_LANGUAGE)", + " CFBundleDisplayName", + " $(PRODUCT_DISPLAY_NAME)", + " CFBundleExecutable", + " $(EXECUTABLE_NAME)", + " CFBundleIdentifier", + " $(PRODUCT_BUNDLE_IDENTIFIER)", + " CFBundleInfoDictionaryVersion", + " 6.0", + " CFBundleName", + " $(PRODUCT_NAME)", + " CFBundlePackageType", + " $(PRODUCT_BUNDLE_PACKAGE_TYPE)", + " CFBundleShortVersionString", + " $(PRODUCT_VERSION)", + " CFBundleURLTypes", + " ", + " ", + " CFBundleURLSchemes", + " ", + " $(PRODUCT_BUNDLE_IDENTIFIER)", + " msauth.$(PRODUCT_BUNDLE_IDENTIFIER)", + " ", + " ", + " ", + " CFBundleVersion", + " $(PRODUCT_BUILD_NUMBER)", + " LSApplicationQueriesSchemes", + " ", + " linkedin", + " ms-outlook", + " ms-sfb", + " msauth", + " msauthv2", + " msauthv3", + " ", + " LSRequiresIPhoneOS", + " ", + " NSAppTransportSecurity", + " ", + " NSAllowsLocalNetworking", + " ", + " ", + " NSCameraUsageDescription", + " The camera is needed to scan QR codes for JS bundle URLs", + " UIAppFonts", + " ", + " OpenType.otf", + " TrueType.ttf", + " ", + " UIApplicationSceneManifest", + " ", + " UIApplicationSupportsMultipleScenes", + " ", + " UISceneConfigurations", + " ", + " UIWindowSceneSessionRoleApplication", + " ", + " ", + " UISceneConfigurationName", + " Default Configuration", + " UISceneDelegateClassName", + " $(PRODUCT_MODULE_NAME).SceneDelegate", + " ", + " ", + " ", + " ", + " UILaunchScreen", + " ", + " UIRequiredDeviceCapabilities", + " ", + " arm64", + " ", + " UISupportedInterfaceOrientations", + " ", + " UIInterfaceOrientationPortrait", + " UIInterfaceOrientationPortraitUpsideDown", + " UIInterfaceOrientationLandscapeLeft", + " UIInterfaceOrientationLandscapeRight", + " ", + " UIViewControllerBasedStatusBarAppearance", + " ", + "", + "", + "", + ], + macos: [ + '', + '', + '', + "", + " ATSApplicationFontsPath", + " .", + " CFBundleDevelopmentRegion", + " $(DEVELOPMENT_LANGUAGE)", + " CFBundleDisplayName", + " $(PRODUCT_DISPLAY_NAME)", + " CFBundleExecutable", + " $(EXECUTABLE_NAME)", + " CFBundleIdentifier", + " $(PRODUCT_BUNDLE_IDENTIFIER)", + " CFBundleInfoDictionaryVersion", + " 6.0", + " CFBundleName", + " $(PRODUCT_DISPLAY_NAME)", + " CFBundlePackageType", + " $(PRODUCT_BUNDLE_PACKAGE_TYPE)", + " CFBundleShortVersionString", + " $(PRODUCT_VERSION)", + " CFBundleVersion", + " $(PRODUCT_BUILD_NUMBER)", + " LSApplicationCategoryType", + " public.app-category.developer-tools", + " LSMinimumSystemVersion", + " $(MACOSX_DEPLOYMENT_TARGET)", + " NSAppTransportSecurity", + " ", + " NSAllowsLocalNetworking", + " ", + " ", + " NSHumanReadableCopyright", + " Copyright © 2020 Microsoft. All rights reserved.", + " NSMainStoryboardFile", + " Main", + " NSPrincipalClass", + " NSApplication", + " NSSupportsAutomaticTermination", + " ", + " NSSupportsSuddenTermination", + " ", + "", + "", + "", + ], + visionos: [ + '', + '', + '', + "", + " CFBundleDevelopmentRegion", + " $(DEVELOPMENT_LANGUAGE)", + " CFBundleDisplayName", + " $(PRODUCT_DISPLAY_NAME)", + " CFBundleExecutable", + " $(EXECUTABLE_NAME)", + " CFBundleIdentifier", + " $(PRODUCT_BUNDLE_IDENTIFIER)", + " CFBundleInfoDictionaryVersion", + " 6.0", + " CFBundleName", + " $(PRODUCT_NAME)", + " CFBundlePackageType", + " $(PRODUCT_BUNDLE_PACKAGE_TYPE)", + " CFBundleShortVersionString", + " $(PRODUCT_VERSION)", + " CFBundleURLTypes", + " ", + " ", + " CFBundleURLSchemes", + " ", + " $(PRODUCT_BUNDLE_IDENTIFIER)", + " msauth.$(PRODUCT_BUNDLE_IDENTIFIER)", + " ", + " ", + " ", + " CFBundleVersion", + " $(PRODUCT_BUILD_NUMBER)", + " LSApplicationQueriesSchemes", + " ", + " linkedin", + " ms-outlook", + " ms-sfb", + " msauth", + " msauthv2", + " msauthv3", + " ", + " LSRequiresIPhoneOS", + " ", + " NSAppTransportSecurity", + " ", + " NSAllowsLocalNetworking", + " ", + " ", + " UIAppFonts", + " ", + " OpenType.otf", + " TrueType.ttf", + " ", + " UIApplicationSceneManifest", + " ", + " UIApplicationSupportsMultipleScenes", + " ", + " UISceneConfigurations", + " ", + " UIWindowSceneSessionRoleApplication", + " ", + " ", + " UISceneConfigurationName", + " Default Configuration", + " UISceneDelegateClassName", + " $(PRODUCT_MODULE_NAME).SceneDelegate", + " ", + " ", + " ", + " ", + " UILaunchScreen", + " ", + " UIRequiredDeviceCapabilities", + " ", + " arm64", + " ", + " UISupportedInterfaceOrientations", + " ", + " UIInterfaceOrientationPortrait", + " UIInterfaceOrientationPortraitUpsideDown", + " UIInterfaceOrientationLandscapeLeft", + " UIInterfaceOrientationLandscapeRight", + " ", + " UIViewControllerBasedStatusBarAppearance", + " ", + "", + "", + "", + ], +}; + +const INFO_PLIST_MACOS = { + applicationCategoryType: [ + '', + '', + '', + "", + " CFBundleDevelopmentRegion", + " $(DEVELOPMENT_LANGUAGE)", + " CFBundleDisplayName", + " $(PRODUCT_DISPLAY_NAME)", + " CFBundleExecutable", + " $(EXECUTABLE_NAME)", + " CFBundleIdentifier", + " $(PRODUCT_BUNDLE_IDENTIFIER)", + " CFBundleInfoDictionaryVersion", + " 6.0", + " CFBundleName", + " $(PRODUCT_DISPLAY_NAME)", + " CFBundlePackageType", + " $(PRODUCT_BUNDLE_PACKAGE_TYPE)", + " CFBundleShortVersionString", + " $(PRODUCT_VERSION)", + " CFBundleVersion", + " $(PRODUCT_BUILD_NUMBER)", + " LSApplicationCategoryType", + " public.app-category.games", + " LSMinimumSystemVersion", + " $(MACOSX_DEPLOYMENT_TARGET)", + " NSAppTransportSecurity", + " ", + " NSAllowsLocalNetworking", + " ", + " ", + " NSHumanReadableCopyright", + " Copyright © 2020 Microsoft. All rights reserved.", + " NSMainStoryboardFile", + " Main", + " NSPrincipalClass", + " NSApplication", + " NSSupportsAutomaticTermination", + " ", + " NSSupportsSuddenTermination", + " ", + "", + "", + "", + ], + humanReadableCopyright: [ + '', + '', + '', + "", + " CFBundleDevelopmentRegion", + " $(DEVELOPMENT_LANGUAGE)", + " CFBundleDisplayName", + " $(PRODUCT_DISPLAY_NAME)", + " CFBundleExecutable", + " $(EXECUTABLE_NAME)", + " CFBundleIdentifier", + " $(PRODUCT_BUNDLE_IDENTIFIER)", + " CFBundleInfoDictionaryVersion", + " 6.0", + " CFBundleName", + " $(PRODUCT_DISPLAY_NAME)", + " CFBundlePackageType", + " $(PRODUCT_BUNDLE_PACKAGE_TYPE)", + " CFBundleShortVersionString", + " $(PRODUCT_VERSION)", + " CFBundleVersion", + " $(PRODUCT_BUILD_NUMBER)", + " LSApplicationCategoryType", + " public.app-category.developer-tools", + " LSMinimumSystemVersion", + " $(MACOSX_DEPLOYMENT_TARGET)", + " NSAppTransportSecurity", + " ", + " NSAllowsLocalNetworking", + " ", + " ", + " NSHumanReadableCopyright", + " Copyright © 2025 Contoso. All rights reserved.", + " NSMainStoryboardFile", + " Main", + " NSPrincipalClass", + " NSApplication", + " NSSupportsAutomaticTermination", + " ", + " NSSupportsSuddenTermination", + " ", + "", + "", + "", + ], +}; diff --git a/test/pack.test.ts b/test/pack.test.ts index 02e016e0a..b29dd1cc3 100644 --- a/test/pack.test.ts +++ b/test/pack.test.ts @@ -155,6 +155,7 @@ describe("npm pack", () => { "ios/assets_catalog.rb", "ios/entitlements.mjs", "ios/entitlements.rb", + "ios/infoPlist.mjs", "ios/info_plist.rb", "ios/node.rb", "ios/pod_helpers.rb",