From ecaf454544e7f6b2e25f38010aad9c23523ea015 Mon Sep 17 00:00:00 2001 From: Tommy Nguyen <4123478+tido64@users.noreply.github.com> Date: Mon, 1 Aug 2022 23:36:20 +0200 Subject: [PATCH 1/4] feat: add support for `@expo/config-plugins` --- example/App.js | 2 +- ios/test_app.rb | 15 ++ package.json | 10 +- scripts/apply-config-plugins.mjs | 33 +++++ scripts/config-plugins/ExpoConfigPlugins.mjs | 10 ++ scripts/config-plugins/apply.mjs | 27 ++++ scripts/config-plugins/index.mjs | 15 ++ .../config-plugins/plugins/mod-compiler.mjs | 27 ++++ .../plugins/withAndroidBaseMods.mjs | 51 +++++++ .../config-plugins/plugins/withInternal.mjs | 19 +++ .../plugins/withIosBaseMods.mjs | 34 +++++ scripts/config-plugins/provider.mjs | 36 +++++ scripts/config-plugins/types.mjs | 11 ++ scripts/validate-manifest.js | 6 +- test-app.gradle | 16 ++- yarn.lock | 128 ++++++++++++++++-- 16 files changed, 422 insertions(+), 18 deletions(-) create mode 100755 scripts/apply-config-plugins.mjs create mode 100644 scripts/config-plugins/ExpoConfigPlugins.mjs create mode 100644 scripts/config-plugins/apply.mjs create mode 100644 scripts/config-plugins/index.mjs create mode 100644 scripts/config-plugins/plugins/mod-compiler.mjs create mode 100644 scripts/config-plugins/plugins/withAndroidBaseMods.mjs create mode 100644 scripts/config-plugins/plugins/withInternal.mjs create mode 100644 scripts/config-plugins/plugins/withIosBaseMods.mjs create mode 100644 scripts/config-plugins/provider.mjs create mode 100644 scripts/config-plugins/types.mjs diff --git a/example/App.js b/example/App.js index 2babfa640..c9cd14d23 100644 --- a/example/App.js +++ b/example/App.js @@ -70,7 +70,7 @@ function useStyles() { groupItemContainer: { alignItems: "center", flexDirection: "row", - marginHorizontal: margin, + paddingHorizontal: margin, }, groupItemLabel: { color: isDarkMode ? Colors.white : Colors.black, diff --git a/ios/test_app.rb b/ios/test_app.rb index c4cf7549f..3b19e3f59 100644 --- a/ios/test_app.rb +++ b/ios/test_app.rb @@ -25,6 +25,19 @@ def app_config(project_root) [manifest['name'], manifest['displayName'], manifest['version'], manifest['singleApp']] end +def apply_config_plugins(project_root) + begin + resolve_module('@expo/config-plugins') + rescue StandardError + # Skip if `@expo/config-plugins` cannot be found + return + end + + apply_config_plugins = File.join(__dir__, '..', 'scripts', 'apply-config-plugins.mjs') + result = system("node \"#{apply_config_plugins}\" \"#{project_root}\"") + raise 'Failed to apply config plugins' unless result +end + def autolink_script_path package_path = resolve_module('@react-native-community/cli-platform-ios') File.join(package_path, 'native_modules') @@ -465,6 +478,8 @@ def use_test_app_internal!(target_platform, options) end end + apply_config_plugins(project_root) + Pod::UI.notice( "`#{xcodeproj}` was sourced from `react-native-test-app`. " \ 'All modifications will be overwritten next time you run `pod install`.' diff --git a/package.json b/package.json index 05c119f15..f1f11611f 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,8 @@ "/macos", "/schema.json", "/scripts/*.js", + "/scripts/apply-config-plugins.mjs", + "/scripts/config-plugins/**/*.mjs", "/windows/*.{js,props,sln}", "/windows/ReactTestApp" ], @@ -42,8 +44,7 @@ "bin": { "configure-test-app": "scripts/configure.js", "init-test-app": "scripts/init.js", - "install-windows-test-app": "windows/test-app.js", - "validate-app-json": "scripts/validate-manifest.js" + "install-windows-test-app": "windows/test-app.js" }, "repository": { "type": "git", @@ -74,6 +75,7 @@ "yargs": "^16.0.0" }, "peerDependencies": { + "@expo/config-plugins": "^5.0.0", "@react-native-community/cli": ">=5.0", "@react-native-community/cli-platform-android": ">=5.0", "@react-native-community/cli-platform-ios": ">=5.0", @@ -84,6 +86,9 @@ "react-native-windows": "^0.0.0-0 || 0.64 - 0.70" }, "peerDependenciesMeta": { + "@expo/config-plugins": { + "optional": true + }, "@react-native-community/cli": { "optional": true }, @@ -107,6 +112,7 @@ "@babel/core": "^7.0.0", "@commitlint/cli": "^17.0.0", "@commitlint/config-conventional": "^17.0.0", + "@expo/config-plugins": "^5.0.0", "@microsoft/eslint-plugin-sdl": "^0.2.0", "@react-native-community/cli": "^7.0.3", "@react-native-community/cli-platform-android": "^7.0.1", diff --git a/scripts/apply-config-plugins.mjs b/scripts/apply-config-plugins.mjs new file mode 100755 index 000000000..24af506ee --- /dev/null +++ b/scripts/apply-config-plugins.mjs @@ -0,0 +1,33 @@ +#!/usr/bin/env node +// @ts-check + +import * as fs from "fs/promises"; +import * as path from "path"; +import { findFile } from "./validate-manifest.js"; + +async function main(projectRoot = process.cwd()) { + const packageJsonPath = findFile("package.json", projectRoot); + if (!packageJsonPath) { + throw new Error("Failed to find `package.json`"); + } + + const content = await fs.readFile(packageJsonPath, { encoding: "utf-8" }); + if (!content.includes('"@expo/config-plugins"')) { + return; + } + + const appJsonPath = findFile("app.json", projectRoot); + if (!appJsonPath) { + return; + } + + const { applyConfigPlugins } = await import("./config-plugins/index.mjs"); + return applyConfigPlugins({ + projectRoot: path.dirname(appJsonPath), + packageJsonPath, + appJsonPath, + }); +} + +const { [2]: projectRoot } = process.argv; +main(projectRoot); diff --git a/scripts/config-plugins/ExpoConfigPlugins.mjs b/scripts/config-plugins/ExpoConfigPlugins.mjs new file mode 100644 index 000000000..346d52cf1 --- /dev/null +++ b/scripts/config-plugins/ExpoConfigPlugins.mjs @@ -0,0 +1,10 @@ +// @ts-check + +// '@expo/config-plugins' is a CommonJS module, which may not support all +// module.exports as named exports. CommonJS modules can always be imported via +// the default export. +import ExpoConfigPlugins from "@expo/config-plugins"; + +const { BaseMods, evalModsAsync, withPlugins } = ExpoConfigPlugins; + +export { BaseMods, evalModsAsync, withPlugins }; diff --git a/scripts/config-plugins/apply.mjs b/scripts/config-plugins/apply.mjs new file mode 100644 index 000000000..ca3a4d2de --- /dev/null +++ b/scripts/config-plugins/apply.mjs @@ -0,0 +1,27 @@ +// @ts-check +import * as fs from "fs/promises"; +import { withPlugins } from "./ExpoConfigPlugins.mjs"; +import { compileModsAsync } from "./plugins/mod-compiler.mjs"; +import { withInternal } from "./plugins/withInternal.mjs"; + +/** + * Applies config plugins. + * @param {import("./types.mjs").ProjectInfo} projectInfo + * @returns {Promise> | undefined>} + */ +export async function applyConfigPlugins({ appJsonPath, ...info }) { + if (!appJsonPath) { + return; + } + + const content = await fs.readFile(appJsonPath, { encoding: "utf-8" }); + const { plugins, ...config } = JSON.parse(content); + if (!Array.isArray(plugins) || plugins.length === 0) { + return; + } + + return compileModsAsync( + withPlugins(withInternal(config, info), plugins), + info + ); +} diff --git a/scripts/config-plugins/index.mjs b/scripts/config-plugins/index.mjs new file mode 100644 index 000000000..e9267e466 --- /dev/null +++ b/scripts/config-plugins/index.mjs @@ -0,0 +1,15 @@ +// @ts-check +import { getAndroidModFileProviders } from "./plugins/withAndroidBaseMods.mjs"; +import { getIosModFileProviders } from "./plugins/withIosBaseMods.mjs"; + +export { applyConfigPlugins } from "./apply.mjs"; +export { + compileModsAsync, + withDefaultBaseMods, +} from "./plugins/mod-compiler.mjs"; +export { withInternal } from "./plugins/withInternal.mjs"; + +export const BaseMods = { + getAndroidModFileProviders, + getIosModFileProviders, +}; diff --git a/scripts/config-plugins/plugins/mod-compiler.mjs b/scripts/config-plugins/plugins/mod-compiler.mjs new file mode 100644 index 000000000..ce237128e --- /dev/null +++ b/scripts/config-plugins/plugins/mod-compiler.mjs @@ -0,0 +1,27 @@ +// @ts-check +import { BaseMods, evalModsAsync } from "../ExpoConfigPlugins.mjs"; +import { getAndroidModFileProviders } from "./withAndroidBaseMods.mjs"; +import { getIosModFileProviders } from "./withIosBaseMods.mjs"; + +/** @type {import("@expo/config-plugins").withDefaultBaseMods} */ +export const withDefaultBaseMods = (config, props) => { + config = BaseMods.withIosBaseMods(config, { + ...props, + providers: getIosModFileProviders(), + }); + config = BaseMods.withAndroidBaseMods(config, { + ...props, + providers: getAndroidModFileProviders(), + }); + return config; +}; + +/** @type {import("@expo/config-plugins").compileModsAsync} */ +export const compileModsAsync = (config, props) => { + if (props.introspect === true) { + console.warn("`introspect` is not supported by react-native-test-app"); + } + + config = withDefaultBaseMods(config); + return evalModsAsync(config, props); +}; diff --git a/scripts/config-plugins/plugins/withAndroidBaseMods.mjs b/scripts/config-plugins/plugins/withAndroidBaseMods.mjs new file mode 100644 index 000000000..074659f67 --- /dev/null +++ b/scripts/config-plugins/plugins/withAndroidBaseMods.mjs @@ -0,0 +1,51 @@ +// @ts-check +import { BaseMods } from "../ExpoConfigPlugins.mjs"; +import { makeFilePathModifier } from "../provider.mjs"; + +const modifyFilePath = makeFilePathModifier( + "node_modules/react-native-test-app/android" +); + +// https://github.com/expo/expo/blob/93cd0503117d5a25f8b80ed7b30ec5bed3a67c24/packages/@expo/config-plugins/src/plugins/withAndroidBaseMods.ts +const expoProviders = BaseMods.getAndroidModFileProviders(); + +/** @type {typeof expoProviders} */ +const defaultProviders = { + dangerous: expoProviders.dangerous, + manifest: modifyFilePath( + expoProviders.manifest, + "app/src/main/AndroidManifest.xml" + ), + gradleProperties: expoProviders.gradleProperties, + strings: modifyFilePath( + expoProviders.strings, + "app/src/main/res/values/strings.xml" + ), + colors: modifyFilePath( + expoProviders.colors, + "app/src/main/res/values/colors.xml" + ), + colorsNight: modifyFilePath( + expoProviders.colors, + "app/src/main/res/values-night/colors.xml" + ), + styles: modifyFilePath( + expoProviders.styles, + "app/src/main/res/values/styles.xml" + ), + projectBuildGradle: expoProviders.projectBuildGradle, + settingsGradle: expoProviders.settingsGradle, + appBuildGradle: expoProviders.appBuildGradle, + mainActivity: modifyFilePath( + expoProviders.mainActivity, + "app/src/main/java/com/microsoft/reacttestapp/MainActivity.kt" + ), + mainApplication: modifyFilePath( + expoProviders.mainApplication, + "app/src/main/java/com/microsoft/reacttestapp/TestApp.kt" + ), +}; + +export function getAndroidModFileProviders() { + return defaultProviders; +} diff --git a/scripts/config-plugins/plugins/withInternal.mjs b/scripts/config-plugins/plugins/withInternal.mjs new file mode 100644 index 000000000..5d9002be5 --- /dev/null +++ b/scripts/config-plugins/plugins/withInternal.mjs @@ -0,0 +1,19 @@ +// @ts-check +/** + * @typedef {import("../types.mjs").ProjectInfo} ProjectInfo + * @typedef {Omit} Internals + */ +/** + * @template Props + * @typedef {import("@expo/config-plugins").ConfigPlugin} ConfigPlugin + */ + +/** @type {ConfigPlugin} */ +export const withInternal = (config, internals) => { + config._internal = { + isDebug: false, + ...config._internal, + ...internals, + }; + return config; +}; diff --git a/scripts/config-plugins/plugins/withIosBaseMods.mjs b/scripts/config-plugins/plugins/withIosBaseMods.mjs new file mode 100644 index 000000000..8a2928b3a --- /dev/null +++ b/scripts/config-plugins/plugins/withIosBaseMods.mjs @@ -0,0 +1,34 @@ +// @ts-check +import { BaseMods } from "../ExpoConfigPlugins.mjs"; +import { makeFilePathModifier, makeNullProvider } from "../provider.mjs"; + +const modifyFilePath = makeFilePathModifier("node_modules/.generated/ios"); + +const nullProvider = makeNullProvider(); + +// https://github.com/expo/expo/blob/93cd0503117d5a25f8b80ed7b30ec5bed3a67c24/packages/@expo/config-plugins/src/plugins/withIosBaseMods.ts +const expoProviders = BaseMods.getIosModFileProviders(); + +/** @type {typeof expoProviders} */ +const defaultProviders = { + dangerous: expoProviders.dangerous, + appDelegate: modifyFilePath( + expoProviders.appDelegate, + "ReactTestApp/AppDelegate.swift" + ), + expoPlist: nullProvider, + xcodeproj: modifyFilePath( + expoProviders.xcodeproj, + "ReactTestApp.xcodeproj/project.pbxproj" + ), + infoPlist: modifyFilePath(expoProviders.infoPlist, "ReactTestApp/Info.plist"), + entitlements: modifyFilePath( + expoProviders.entitlements, + "ReactTestApp/ReactTestApp.entitlements" + ), + podfileProperties: nullProvider, +}; + +export function getIosModFileProviders() { + return defaultProviders; +} diff --git a/scripts/config-plugins/provider.mjs b/scripts/config-plugins/provider.mjs new file mode 100644 index 000000000..446961c1b --- /dev/null +++ b/scripts/config-plugins/provider.mjs @@ -0,0 +1,36 @@ +// @ts-check +import * as path from "path"; +import { findFile } from "../validate-manifest.js"; +import { BaseMods } from "./ExpoConfigPlugins.mjs"; + +/** + * @template ModType + * @template Props + * @typedef {ReturnType>} BaseModProviderMethods + */ + +export function makeNullProvider(defaultRead = {}) { + return BaseMods.provider({ + getFilePath: () => "", + read: () => Promise.resolve(defaultRead), + write: () => Promise.resolve(), + }); +} + +/** + * Creates a mod modifier that just changes `getFilePath()`. + * @param {string} actualProjectDir + * @returns {(original: BaseModProviderMethods, file: string) => BaseModProviderMethods} + */ +export function makeFilePathModifier(actualProjectDir) { + return function (original, file) { + return BaseMods.provider({ + ...original, + getFilePath: async ({ modRequest: { projectRoot } }) => { + const name = path.posix.join(actualProjectDir, file); + const result = findFile(name, projectRoot); + return result || name; + }, + }); + }; +} diff --git a/scripts/config-plugins/types.mjs b/scripts/config-plugins/types.mjs new file mode 100644 index 000000000..03924b5de --- /dev/null +++ b/scripts/config-plugins/types.mjs @@ -0,0 +1,11 @@ +// @ts-check +/** + * @typedef {{ + * projectRoot: string; + * packageJsonPath: string; + * appJsonPath: string; + * }} ProjectInfo + */ + +/** This dummy export is only here so that JSDoc can import this module. */ +export const __dummy = 0; diff --git a/scripts/validate-manifest.js b/scripts/validate-manifest.js index 89e7701da..a9ad241c9 100755 --- a/scripts/validate-manifest.js +++ b/scripts/validate-manifest.js @@ -14,6 +14,7 @@ const BUILD_PROPS = [ "ios", "macos", "windows", + "plugins", "resources", ]; @@ -146,8 +147,3 @@ function validate(outputMode = "stdout", projectRoot = process.cwd()) { exports.findFile = findFile; exports.validate = validate; exports.validateManifest = validateManifest; - -if (require.main === module) { - // istanbul ignore next - validate(); -} diff --git a/test-app.gradle b/test-app.gradle index ab8793c1f..8801d3a9d 100644 --- a/test-app.gradle +++ b/test-app.gradle @@ -4,13 +4,23 @@ import org.gradle.initialization.DefaultSettings import java.nio.file.Paths +private static void applyConfigPlugins(String testAppDir, File rootDir) { + String[] patch = ["node", "${testAppDir}/scripts/apply-config-plugins.mjs"] + def stderr = new StringBuffer() + def proc = Runtime.runtime.exec(patch, null, rootDir) + proc.waitForProcessOutput(null, stderr) + if (proc.exitValue() != 0) { + throw new RuntimeException("Failed to apply config plugins:\n${stderr}") + } +} + // TODO: Remove when `@react-native-community/cli` 6.0+ is required. See also // https://github.com/react-native-community/cli/commit/fa0d09b2c9be144bbdff526bb14f171d7ddca88e private static void patchArgumentTypeMismatchError(String testAppDir, File rootDir) { // We need to delegate this to a separate script to avoid running out of // Java heap space. String[] patch = ["node", "${testAppDir}/scripts/patch-cli-platform-android.js"] - Runtime.getRuntime().exec(patch, null, rootDir).waitFor() + Runtime.runtime.exec(patch, null, rootDir).waitFor() } def testAppDir = buildscript.sourceFile.getParent() @@ -18,6 +28,10 @@ apply(from: "${testAppDir}/android/test-app-util.gradle") patchArgumentTypeMismatchError(testAppDir, rootDir) +if (findNodeModulesPath("@expo/config-plugins", rootDir)) { + applyConfigPlugins(testAppDir, rootDir) +} + def cliAndroidDir = findNodeModulesPath("@react-native-community/cli-platform-android", rootDir) apply(from: "${cliAndroidDir}/native_modules.gradle") diff --git a/yarn.lock b/yarn.lock index ba49bc662..b93e9d244 100644 --- a/yarn.lock +++ b/yarn.lock @@ -83,6 +83,15 @@ __metadata: languageName: node linkType: hard +"@babel/code-frame@npm:~7.10.4": + version: 7.10.4 + resolution: "@babel/code-frame@npm:7.10.4" + dependencies: + "@babel/highlight": ^7.10.4 + checksum: feb4543c8a509fe30f0f6e8d7aa84f82b41148b963b826cd330e34986f649a85cb63b2f13dd4effdf434ac555d16f14940b8ea5f4433297c2f5ff85486ded019 + languageName: node + linkType: hard + "@babel/compat-data@npm:^7.13.11, @babel/compat-data@npm:^7.15.0, @babel/compat-data@npm:^7.19.3": version: 7.19.3 resolution: "@babel/compat-data@npm:7.19.3" @@ -384,7 +393,7 @@ __metadata: languageName: node linkType: hard -"@babel/highlight@npm:^7.18.6": +"@babel/highlight@npm:^7.10.4, @babel/highlight@npm:^7.18.6": version: 7.18.6 resolution: "@babel/highlight@npm:7.18.6" dependencies: @@ -1346,6 +1355,65 @@ __metadata: languageName: node linkType: hard +"@expo/config-plugins@npm:^5.0.0": + version: 5.0.1 + resolution: "@expo/config-plugins@npm:5.0.1" + dependencies: + "@expo/config-types": ^46.0.0 + "@expo/json-file": 8.2.36 + "@expo/plist": 0.0.18 + "@expo/sdk-runtime-versions": ^1.0.0 + "@react-native/normalize-color": ^2.0.0 + chalk: ^4.1.2 + debug: ^4.3.1 + find-up: ~5.0.0 + getenv: ^1.0.0 + glob: 7.1.6 + resolve-from: ^5.0.0 + semver: ^7.3.5 + slash: ^3.0.0 + xcode: ^3.0.1 + xml2js: 0.4.23 + checksum: bd7139ba8728e7117aaa1ae056190ed69bfde707286d13ef2fe26201381753e6d2066a223eb69547825ab1469c253a75f2e73fad82021b2a7589bb98fd8bf8bb + languageName: node + linkType: hard + +"@expo/config-types@npm:^46.0.0": + version: 46.0.2 + resolution: "@expo/config-types@npm:46.0.2" + checksum: ea7b3c64e875dfe65209e5f099b3ef4ddc464f308aff50ba08c26c1dfc3d3c177203190b29b5d7dd334932d74f765690c44829d637d7a71a72078214c69a07ab + languageName: node + linkType: hard + +"@expo/json-file@npm:8.2.36": + version: 8.2.36 + resolution: "@expo/json-file@npm:8.2.36" + dependencies: + "@babel/code-frame": ~7.10.4 + json5: ^1.0.1 + write-file-atomic: ^2.3.0 + checksum: 37ce80b3472fef2a56136ebff5993d98ab4fbd45c4d7791ff47be80438dbeabd84bc699a401da0c314357ef65d8fff87a5a1241b3119db2d575878f9321bd1e7 + languageName: node + linkType: hard + +"@expo/plist@npm:0.0.18": + version: 0.0.18 + resolution: "@expo/plist@npm:0.0.18" + dependencies: + "@xmldom/xmldom": ~0.7.0 + base64-js: ^1.2.3 + xmlbuilder: ^14.0.0 + checksum: 42f5743fcd2a07b55a9f048d27cf0f273510ab35dde1f7030b22dc8c30ab2cfb65c6e68f8aa58fbcfa00177fdc7c9696d0004083c9a47c36fd4ac7fea27d6ccc + languageName: node + linkType: hard + +"@expo/sdk-runtime-versions@npm:^1.0.0": + version: 1.0.0 + resolution: "@expo/sdk-runtime-versions@npm:1.0.0" + checksum: 0942d5a356f590e8dc795761456cc48b3e2d6a38ad2a02d6774efcdc5a70424e05623b4e3e5d2fec0cdc30f40dde05c14391c781607eed3971bf8676518bfd9d + languageName: node + linkType: hard + "@gar/promisify@npm:^1.0.1, @gar/promisify@npm:^1.1.3": version: 1.1.3 resolution: "@gar/promisify@npm:1.1.3" @@ -2423,7 +2491,7 @@ __metadata: languageName: node linkType: hard -"@react-native/normalize-color@npm:*, @react-native/normalize-color@npm:2.0.0": +"@react-native/normalize-color@npm:*, @react-native/normalize-color@npm:2.0.0, @react-native/normalize-color@npm:^2.0.0": version: 2.0.0 resolution: "@react-native/normalize-color@npm:2.0.0" checksum: 2da373297f0d22b700edb9ab1b2cca34684e94a5dfe172e1cfd114e74ac17e139e802bc671e9868e0a580190eccbf3fa804f67be8cc1d9cbd0e216e994495931 @@ -3008,7 +3076,7 @@ __metadata: languageName: node linkType: hard -"@xmldom/xmldom@npm:^0.7.5": +"@xmldom/xmldom@npm:^0.7.5, @xmldom/xmldom@npm:~0.7.0": version: 0.7.5 resolution: "@xmldom/xmldom@npm:0.7.5" checksum: 8d7ec35c1ef6183b4f621df08e01d7e61f244fb964a4719025e65fe6ac06fac418919be64fb40fe5908e69158ef728f2d936daa082db326fe04603012b5f2a84 @@ -3710,7 +3778,7 @@ __metadata: languageName: node linkType: hard -"base64-js@npm:^1.1.2, base64-js@npm:^1.3.1, base64-js@npm:^1.5.1": +"base64-js@npm:^1.1.2, base64-js@npm:^1.2.3, base64-js@npm:^1.3.1, base64-js@npm:^1.5.1": version: 1.5.1 resolution: "base64-js@npm:1.5.1" checksum: 669632eb3745404c2f822a18fc3a0122d2f9a7a13f7fb8b5823ee19d1d2ff9ee5b52c53367176ea4ad093c332fd5ab4bd0ebae5a8e27917a4105a4cfc86b1005 @@ -4721,7 +4789,7 @@ __metadata: languageName: node linkType: hard -"debug@npm:4, debug@npm:^4.0.0, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.2, debug@npm:^4.3.3": +"debug@npm:4, debug@npm:^4.0.0, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.3": version: 4.3.4 resolution: "debug@npm:4.3.4" dependencies: @@ -5853,7 +5921,7 @@ __metadata: languageName: node linkType: hard -"find-up@npm:^5.0.0": +"find-up@npm:^5.0.0, find-up@npm:~5.0.0": version: 5.0.0 resolution: "find-up@npm:5.0.0" dependencies: @@ -6166,6 +6234,13 @@ fsevents@^2.3.2: languageName: node linkType: hard +"getenv@npm:^1.0.0": + version: 1.0.0 + resolution: "getenv@npm:1.0.0" + checksum: 19ae5cad603a1cf1bcb8fa3bed48e00d062eb0572a4404c02334b67f3b3499f238383082b064bb42515e9e25c2b08aef1a3e3d2b6852347721aa8b174825bd56 + languageName: node + linkType: hard + "git-log-parser@npm:^1.2.0": version: 1.2.0 resolution: "git-log-parser@npm:1.2.0" @@ -6213,6 +6288,20 @@ fsevents@^2.3.2: languageName: node linkType: hard +"glob@npm:7.1.6": + version: 7.1.6 + resolution: "glob@npm:7.1.6" + dependencies: + fs.realpath: ^1.0.0 + inflight: ^1.0.4 + inherits: 2 + minimatch: ^3.0.4 + once: ^1.3.0 + path-is-absolute: ^1.0.0 + checksum: 351d549dd90553b87c2d3f90ce11aed9e1093c74130440e7ae0592e11bbcd2ce7f0ebb8ba6bfe63aaf9b62166a7f4c80cb84490ae5d78408bb2572bf7d4ee0a6 + languageName: node + linkType: hard + "glob@npm:^7.0.0, glob@npm:^7.1.1, glob@npm:^7.1.2, glob@npm:^7.1.3, glob@npm:^7.1.4": version: 7.1.7 resolution: "glob@npm:7.1.7" @@ -8017,6 +8106,17 @@ fsevents@^2.3.2: languageName: node linkType: hard +"json5@npm:^1.0.1": + version: 1.0.1 + resolution: "json5@npm:1.0.1" + dependencies: + minimist: ^1.2.0 + bin: + json5: lib/cli.js + checksum: e76ea23dbb8fc1348c143da628134a98adf4c5a4e8ea2adaa74a80c455fc2cdf0e2e13e6398ef819bfe92306b610ebb2002668ed9fc1af386d593691ef346fc3 + languageName: node + linkType: hard + "json5@npm:^2.2.1": version: 2.2.1 resolution: "json5@npm:2.2.1" @@ -10776,6 +10876,7 @@ fsevents@^2.3.2: "@babel/core": ^7.0.0 "@commitlint/cli": ^17.0.0 "@commitlint/config-conventional": ^17.0.0 + "@expo/config-plugins": ^5.0.0 "@microsoft/eslint-plugin-sdl": ^0.2.0 "@react-native-community/cli": ^7.0.3 "@react-native-community/cli-platform-android": ^7.0.1 @@ -10806,6 +10907,7 @@ fsevents@^2.3.2: uuid: ^8.3.2 yargs: ^16.0.0 peerDependencies: + "@expo/config-plugins": ^5.0.0 "@react-native-community/cli": ">=5.0" "@react-native-community/cli-platform-android": ">=5.0" "@react-native-community/cli-platform-ios": ">=5.0" @@ -10815,6 +10917,8 @@ fsevents@^2.3.2: react-native-macos: ^0.0.0-0 || 0.64 || 0.66 || 0.68 react-native-windows: ^0.0.0-0 || 0.64 - 0.70 peerDependenciesMeta: + "@expo/config-plugins": + optional: true "@react-native-community/cli": optional: true "@react-native-community/cli-platform-android": @@ -10831,7 +10935,6 @@ fsevents@^2.3.2: configure-test-app: scripts/configure.js init-test-app: scripts/init.js install-windows-test-app: windows/test-app.js - validate-app-json: scripts/validate-manifest.js languageName: unknown linkType: soft @@ -13297,7 +13400,7 @@ resolve@^2.0.0-next.3: languageName: node linkType: hard -"xcode@npm:^3.0.0": +"xcode@npm:^3.0.0, xcode@npm:^3.0.1": version: 3.0.1 resolution: "xcode@npm:3.0.1" dependencies: @@ -13339,7 +13442,7 @@ resolve@^2.0.0-next.3: languageName: node linkType: hard -"xml2js@npm:^0.4.19": +"xml2js@npm:0.4.23, xml2js@npm:^0.4.19": version: 0.4.23 resolution: "xml2js@npm:0.4.23" dependencies: @@ -13349,6 +13452,13 @@ resolve@^2.0.0-next.3: languageName: node linkType: hard +"xmlbuilder@npm:^14.0.0": + version: 14.0.0 + resolution: "xmlbuilder@npm:14.0.0" + checksum: 9e93d3c73957dbb21acde63afa5d241b19057bdbdca9d53534d8351e70f1d5c9db154e3ca19bd3e9ea84c082539ab6e7845591c8778a663e8b5d3470d5427a8b + languageName: node + linkType: hard + "xmlbuilder@npm:^9.0.7": version: 9.0.7 resolution: "xmlbuilder@npm:9.0.7" From 9eacd4777f97fa9c48111e76b7e73301b9b72815 Mon Sep 17 00:00:00 2001 From: Tommy Nguyen <4123478+tido64@users.noreply.github.com> Date: Sat, 20 Aug 2022 10:02:11 +0200 Subject: [PATCH 2/4] add anchor points --- ios/ReactTestApp/AppDelegate.swift | 39 ++++++++-- .../Public/ReactTestApp-DevSupport.h | 2 + ios/ReactTestApp/ReactTestApp-DevSupport.m | 2 + ios/ReactTestApp/SceneDelegate.swift | 33 ++++++--- macos/ReactTestApp/AppDelegate.swift | 71 ++++++++++++------- package.json | 2 +- 6 files changed, 108 insertions(+), 41 deletions(-) diff --git a/ios/ReactTestApp/AppDelegate.swift b/ios/ReactTestApp/AppDelegate.swift index 794137abb..1573483f8 100644 --- a/ios/ReactTestApp/AppDelegate.swift +++ b/ios/ReactTestApp/AppDelegate.swift @@ -13,7 +13,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { } func application(_ application: UIApplication, - didFinishLaunchingWithOptions _: [UIApplication.LaunchOptionsKey: Any]?) -> Bool + didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { self.application = application @@ -24,21 +24,50 @@ class AppDelegate: UIResponder, UIApplicationDelegate { ) } + // application(_:didFinishLaunchingWithOptions:) + return true } - // MARK: UISceneSession Lifecycle + func applicationWillTerminate(_ application: UIApplication) { + // applicationWillTerminate(_:) + } + + // MARK: Push Notifications + + func application(_ application: UIApplication, + didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) + { + // application(_:didRegisterForRemoteNotificationsWithDeviceToken:) + } - func application(_: UIApplication, + func application(_ application: UIApplication, + didFailToRegisterForRemoteNotificationsWithError error: Error) + { + // application(_:didFailToRegisterForRemoteNotificationsWithError:) + } + + func application(_ application: UIApplication, + didReceiveRemoteNotification userInfo: [AnyHashable: Any], + fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) + { + // application(_:didReceiveRemoteNotification:fetchCompletionHandler:) + } + + // MARK: UISceneSession Support + + func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, - options _: UIScene.ConnectionOptions) -> UISceneConfiguration + options: UIScene.ConnectionOptions) -> UISceneConfiguration { // Called when a new scene session is being created. // Use this method to select a configuration to create the new scene with. UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) } - func application(_: UIApplication, didDiscardSceneSessions _: Set) { + func application(_ application: UIApplication, + didDiscardSceneSessions sceneSessions: Set) + { // Called when the user discards a scene session. // If any sessions were discarded while the application was not running, this will be called shortly after // application:didFinishLaunchingWithOptions. diff --git a/ios/ReactTestApp/Public/ReactTestApp-DevSupport.h b/ios/ReactTestApp/Public/ReactTestApp-DevSupport.h index 4511e4ccb..e5fb34634 100644 --- a/ios/ReactTestApp/Public/ReactTestApp-DevSupport.h +++ b/ios/ReactTestApp/Public/ReactTestApp-DevSupport.h @@ -3,9 +3,11 @@ NS_ASSUME_NONNULL_BEGIN extern NSNotificationName const ReactTestAppDidInitializeNotification; + extern NSNotificationName const ReactTestAppWillInitializeReactNativeNotification; extern NSNotificationName const ReactTestAppDidInitializeReactNativeNotification; extern NSNotificationName const ReactTestAppDidRegisterAppsNotification; + extern NSNotificationName const ReactTestAppSceneDidOpenURLNotification; NS_ASSUME_NONNULL_END diff --git a/ios/ReactTestApp/ReactTestApp-DevSupport.m b/ios/ReactTestApp/ReactTestApp-DevSupport.m index f7a5a489e..9a60bd327 100644 --- a/ios/ReactTestApp/ReactTestApp-DevSupport.m +++ b/ios/ReactTestApp/ReactTestApp-DevSupport.m @@ -2,11 +2,13 @@ NSNotificationName const ReactTestAppDidInitializeNotification = @"ReactTestAppDidInitializeNotification"; + NSNotificationName const ReactTestAppWillInitializeReactNativeNotification = @"ReactTestAppWillInitializeReactNativeNotification"; NSNotificationName const ReactTestAppDidInitializeReactNativeNotification = @"ReactTestAppDidInitializeReactNativeNotification"; NSNotificationName const ReactTestAppDidRegisterAppsNotification = @"ReactTestAppDidRegisterAppsNotification"; + NSNotificationName const ReactTestAppSceneDidOpenURLNotification = @"ReactTestAppSceneDidOpenURLNotification"; diff --git a/ios/ReactTestApp/SceneDelegate.swift b/ios/ReactTestApp/SceneDelegate.swift index 1c63cc130..9db6b76c0 100644 --- a/ios/ReactTestApp/SceneDelegate.swift +++ b/ios/ReactTestApp/SceneDelegate.swift @@ -6,33 +6,38 @@ final class SceneDelegate: UIResponder, UIWindowSceneDelegate { private lazy var reactInstance = ReactInstance() - func sceneDidDisconnect(_: UIScene) { + func sceneDidDisconnect(_ scene: UIScene) { // Called as the scene is being released by the system. // This occurs shortly after the scene enters the background, or when its session is discarded. // Release any resources associated with this scene that can be re-created the next time the scene connects. // The scene may re-connect later, as its session was not neccessarily discarded (see // `application:didDiscardSceneSessions` instead). + // sceneDidDisconnect(_:) } - func sceneDidBecomeActive(_: UIScene) { + func sceneDidBecomeActive(_ scene: UIScene) { // Called when the scene has moved from an inactive state to an active state. // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. + // sceneDidBecomeActive(_:) } - func sceneWillResignActive(_: UIScene) { + func sceneWillResignActive(_ scene: UIScene) { // Called when the scene will move from an active state to an inactive state. // This may occur due to temporary interruptions (ex. an incoming phone call). + // sceneWillResignActive(_:) } - func sceneWillEnterForeground(_: UIScene) { + func sceneWillEnterForeground(_ scene: UIScene) { // Called as the scene transitions from the background to the foreground. // Use this method to undo the changes made on entering the background. + // sceneWillEnterForeground(_:) } - func sceneDidEnterBackground(_: UIScene) { + func sceneDidEnterBackground(_ scene: UIScene) { // Called as the scene transitions from the foreground to the background. // Use this method to save data, release shared resources, and store enough scene-specific state information // to restore the scene back to its current state. + // sceneDidEnterBackground(_:) } func scene(_ scene: UIScene, openURLContexts URLContexts: Set) { @@ -51,6 +56,12 @@ final class SceneDelegate: UIResponder, UIWindowSceneDelegate { "URLContexts": URLContexts, ] ) + + // scene(_:openURLContexts:) + } + + func scene(_ scene: UIScene, continue userActivity: NSUserActivity) { + // scene(_:continue:) } } @@ -65,8 +76,8 @@ extension SceneDelegate { } func scene(_ scene: UIScene, - willConnectTo _: UISceneSession, - options _: UIScene.ConnectionOptions) + willConnectTo session: UISceneSession, + options connectionOptions: UIScene.ConnectionOptions) { // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene // `scene`. @@ -86,6 +97,8 @@ extension SceneDelegate { self.window = window window.makeKeyAndVisible() } + + // scene(_:willConnectTo:options:) } } @@ -97,8 +110,8 @@ extension SceneDelegate { extension SceneDelegate { func scene(_ scene: UIScene, - willConnectTo _: UISceneSession, - options _: UIScene.ConnectionOptions) + willConnectTo session: UISceneSession, + options connectionOptions: UIScene.ConnectionOptions) { guard let windowScene = scene as? UIWindowScene else { assertionFailure("Default scene configuration should have been loaded by now") @@ -120,6 +133,8 @@ extension SceneDelegate { self.window = window window.makeKeyAndVisible() + + // scene(_:willConnectTo:options:) } } diff --git a/macos/ReactTestApp/AppDelegate.swift b/macos/ReactTestApp/AppDelegate.swift index f2db30da7..d788a48ac 100644 --- a/macos/ReactTestApp/AppDelegate.swift +++ b/macos/ReactTestApp/AppDelegate.swift @@ -20,11 +20,42 @@ final class AppDelegate: NSObject, NSApplicationDelegate { true } + func applicationDidFinishLaunching(_: Notification) { + NotificationCenter.default.post( + name: .ReactTestAppDidInitialize, + object: nil + ) + + initialize() + + // applicationDidFinishLaunching(_:) + } + func applicationWillTerminate(_: Notification) { - // Insert code here to tear down your application + // applicationWillTerminate(_:) + } + + // MARK: Push Notifications + + func application(_ application: NSApplication, + didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) + { + // application(_:didRegisterForRemoteNotificationsWithDeviceToken:) + } + + func application(_ application: NSApplication, + didFailToRegisterForRemoteNotificationsWithError error: Error) + { + // application(_:didFailToRegisterForRemoteNotificationsWithError:) } - // MARK: - User interaction + func application(_ application: NSApplication, + didReceiveRemoteNotification userInfo: [String: Any]) + { + // application(_:didReceiveRemoteNotification:) + } + + // MARK: User interaction @IBAction func onLoadEmbeddedBundleSelected(_: NSMenuItem) { @@ -50,7 +81,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate { } } - // MARK: - Private + // MARK: Private private enum WindowSize { static let defaultSize = CGSize(width: 640, height: 480) @@ -76,22 +107,7 @@ extension AppDelegate { !(mainWindow?.contentViewController is ViewController) } - func applicationWillFinishLaunching(_: Notification) { - if Session.shouldRememberLastComponent { - rememberLastComponentMenuItem.state = .on - } - - showReactMenu() - } - - func applicationDidFinishLaunching(_: Notification) { - defer { - NotificationCenter.default.post( - name: .ReactTestAppDidInitialize, - object: nil - ) - } - + func initialize() { guard let (manifest, checksum) = Manifest.fromFile() else { let item = reactMenu.addItem( withTitle: "Could not load 'app.json'", @@ -149,6 +165,14 @@ extension AppDelegate { manifestChecksum = checksum } + func applicationWillFinishLaunching(_: Notification) { + if Session.shouldRememberLastComponent { + rememberLastComponentMenuItem.state = .on + } + + showReactMenu() + } + @objc private func onComponentSelected(menuItem: NSMenuItem) { guard let component = menuItem.representedObject as? Component else { @@ -257,6 +281,8 @@ extension AppDelegate { #if ENABLE_SINGLE_APP_MODE extension AppDelegate { + func initialize() {} + func applicationWillFinishLaunching(_: Notification) { guard let window = mainWindow else { assertionFailure("Main window should have been instantiated by now") @@ -282,13 +308,6 @@ extension AppDelegate { showReactMenu() #endif // DEBUG } - - func applicationDidFinishLaunching(_: Notification) { - NotificationCenter.default.post( - name: .ReactTestAppDidInitialize, - object: nil - ) - } } #endif // ENABLE_SINGLE_APP_MODE diff --git a/package.json b/package.json index f1f11611f..040dd23cb 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,7 @@ "clean": "git clean -dfqx --exclude=.yarn/cache", "format:c": "clang-format -i $(git ls-files '*.cpp' '*.h' '*.m' '*.mm')", "format:js": "prettier --write $(git ls-files '*.js' '*.yml' 'test/**/*.json')", - "format:swift": "swiftformat --swiftversion 5.5 --ifdef no-indent ios macos", + "format:swift": "swiftformat --swiftversion 5.5 --ifdef no-indent --stripunusedargs closure-only ios macos", "generate:code": "node scripts/generate-manifest.mjs", "generate:schema": "node scripts/generate-schema.mjs", "lint:commit": "git log --format='%s' origin/trunk..HEAD | tail -1 | commitlint", From 794ee53b3ebda23560d5dfacd7feb67e6c861e70 Mon Sep 17 00:00:00 2001 From: Tommy Nguyen <4123478+tido64@users.noreply.github.com> Date: Mon, 22 Aug 2022 14:54:41 +0200 Subject: [PATCH 3/4] add SceneDelegate.swift --- scripts/config-plugins/plugins/withIosBaseMods.mjs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/scripts/config-plugins/plugins/withIosBaseMods.mjs b/scripts/config-plugins/plugins/withIosBaseMods.mjs index 8a2928b3a..da547a4a7 100644 --- a/scripts/config-plugins/plugins/withIosBaseMods.mjs +++ b/scripts/config-plugins/plugins/withIosBaseMods.mjs @@ -29,6 +29,12 @@ const defaultProviders = { podfileProperties: nullProvider, }; +// `react-native-test-app` files +defaultProviders["sceneDelegate"] = modifyFilePath( + expoProviders.appDelegate, + "ReactTestApp/SceneDelegate.swift" +); + export function getIosModFileProviders() { return defaultProviders; } From 1301fd71dc29b8480c724a792119ceaeb69f33f2 Mon Sep 17 00:00:00 2001 From: Tommy Nguyen <4123478+tido64@users.noreply.github.com> Date: Tue, 13 Sep 2022 16:17:09 +0200 Subject: [PATCH 4/4] dedupe --- package.json | 3 ++- yarn.lock | 26 ++++++-------------------- 2 files changed, 8 insertions(+), 21 deletions(-) diff --git a/package.json b/package.json index 040dd23cb..cb6e66e4f 100644 --- a/package.json +++ b/package.json @@ -142,7 +142,8 @@ }, "packageManager": "yarn@3.1.1", "resolutions": { - "@commitlint/is-ignored/semver": "^7.3.5", + "@commitlint/is-ignored/semver": "^7.3.7", + "@expo/config-plugins/glob": "^7.1.6", "@microsoft/eslint-plugin-sdl/eslint-plugin-react": "^7.26.0", "core-js-compat/semver": "^7.3.5" }, diff --git a/yarn.lock b/yarn.lock index b93e9d244..734e44a51 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6288,31 +6288,17 @@ fsevents@^2.3.2: languageName: node linkType: hard -"glob@npm:7.1.6": - version: 7.1.6 - resolution: "glob@npm:7.1.6" +"glob@npm:^7.0.0, glob@npm:^7.1.1, glob@npm:^7.1.2, glob@npm:^7.1.3, glob@npm:^7.1.4, glob@npm:^7.1.6": + version: 7.2.3 + resolution: "glob@npm:7.2.3" dependencies: fs.realpath: ^1.0.0 inflight: ^1.0.4 inherits: 2 - minimatch: ^3.0.4 - once: ^1.3.0 - path-is-absolute: ^1.0.0 - checksum: 351d549dd90553b87c2d3f90ce11aed9e1093c74130440e7ae0592e11bbcd2ce7f0ebb8ba6bfe63aaf9b62166a7f4c80cb84490ae5d78408bb2572bf7d4ee0a6 - languageName: node - linkType: hard - -"glob@npm:^7.0.0, glob@npm:^7.1.1, glob@npm:^7.1.2, glob@npm:^7.1.3, glob@npm:^7.1.4": - version: 7.1.7 - resolution: "glob@npm:7.1.7" - dependencies: - fs.realpath: ^1.0.0 - inflight: ^1.0.4 - inherits: 2 - minimatch: ^3.0.4 + minimatch: ^3.1.1 once: ^1.3.0 path-is-absolute: ^1.0.0 - checksum: b61f48973bbdcf5159997b0874a2165db572b368b931135832599875919c237fc05c12984e38fe828e69aa8a921eb0e8a4997266211c517c9cfaae8a93988bb8 + checksum: 29452e97b38fa704dabb1d1045350fb2467cf0277e155aa9ff7077e90ad81d1ea9d53d3ee63bd37c05b09a065e90f16aec4a65f5b8de401d1dac40bc5605d133 languageName: node linkType: hard @@ -9201,7 +9187,7 @@ fsevents@^2.3.2: languageName: node linkType: hard -"minimatch@npm:^3.0.2, minimatch@npm:^3.0.4, minimatch@npm:^3.1.2": +"minimatch@npm:^3.0.2, minimatch@npm:^3.0.4, minimatch@npm:^3.1.1, minimatch@npm:^3.1.2": version: 3.1.2 resolution: "minimatch@npm:3.1.2" dependencies: