diff --git a/docs/generated/manifests/menus.json b/docs/generated/manifests/menus.json index 1360c31836..d40c6c527e 100644 --- a/docs/generated/manifests/menus.json +++ b/docs/generated/manifests/menus.json @@ -5743,6 +5743,14 @@ "isExternal": false, "disableCollapsible": false }, + { + "id": "build-ios", + "path": "/packages/react-native/executors/build-ios", + "name": "build-ios", + "children": [], + "isExternal": false, + "disableCollapsible": false + }, { "id": "start", "path": "/packages/react-native/executors/start", @@ -5774,6 +5782,14 @@ "children": [], "isExternal": false, "disableCollapsible": false + }, + { + "id": "pod-install", + "path": "/packages/react-native/executors/pod-install", + "name": "pod-install", + "children": [], + "isExternal": false, + "disableCollapsible": false } ], "isExternal": false, diff --git a/docs/generated/manifests/packages.json b/docs/generated/manifests/packages.json index 5ed45108f1..4a32dbdc95 100644 --- a/docs/generated/manifests/packages.json +++ b/docs/generated/manifests/packages.json @@ -2213,6 +2213,15 @@ "path": "/packages/react-native/executors/build-android", "type": "executor" }, + "/packages/react-native/executors/build-ios": { + "description": "Build iOS app", + "file": "generated/packages/react-native/executors/build-ios.json", + "hidden": false, + "name": "build-ios", + "originalFilePath": "/packages/react-native/src/executors/build-ios/schema.json", + "path": "/packages/react-native/executors/build-ios", + "type": "executor" + }, "/packages/react-native/executors/start": { "description": "Starts the Javascript server that communicates with connected devices.", "file": "generated/packages/react-native/executors/start.json", @@ -2248,6 +2257,15 @@ "originalFilePath": "/packages/react-native/src/executors/storybook/schema.json", "path": "/packages/react-native/executors/storybook", "type": "executor" + }, + "/packages/react-native/executors/pod-install": { + "description": "Run `pod install` in the `ios` directory.", + "file": "generated/packages/react-native/executors/pod-install.json", + "hidden": false, + "name": "pod-install", + "originalFilePath": "/packages/react-native/src/executors/pod-install/schema.json", + "path": "/packages/react-native/executors/pod-install", + "type": "executor" } }, "generators": { diff --git a/docs/generated/packages-metadata.json b/docs/generated/packages-metadata.json index a97571dc94..89a282adff 100644 --- a/docs/generated/packages-metadata.json +++ b/docs/generated/packages-metadata.json @@ -2186,6 +2186,15 @@ "path": "react-native/executors/build-android", "type": "executor" }, + { + "description": "Build iOS app", + "file": "generated/packages/react-native/executors/build-ios.json", + "hidden": false, + "name": "build-ios", + "originalFilePath": "/packages/react-native/src/executors/build-ios/schema.json", + "path": "react-native/executors/build-ios", + "type": "executor" + }, { "description": "Starts the Javascript server that communicates with connected devices.", "file": "generated/packages/react-native/executors/start.json", @@ -2221,6 +2230,15 @@ "originalFilePath": "/packages/react-native/src/executors/storybook/schema.json", "path": "react-native/executors/storybook", "type": "executor" + }, + { + "description": "Run `pod install` in the `ios` directory.", + "file": "generated/packages/react-native/executors/pod-install.json", + "hidden": false, + "name": "pod-install", + "originalFilePath": "/packages/react-native/src/executors/pod-install/schema.json", + "path": "react-native/executors/pod-install", + "type": "executor" } ], "generators": [ diff --git a/docs/generated/packages/react-native/documents/overview.md b/docs/generated/packages/react-native/documents/overview.md index a183575fcf..4c58fdabd4 100644 --- a/docs/generated/packages/react-native/documents/overview.md +++ b/docs/generated/packages/react-native/documents/overview.md @@ -52,7 +52,7 @@ yarn add --dev @nrwl/react-native To create additional React Native apps run: ```shell -nx g @nrwl/react-native:app your-app-name +nx g @nrwl/react-native:app ``` ### Generating Libraries @@ -60,7 +60,7 @@ nx g @nrwl/react-native:app your-app-name To generate a new library run: ```shell -npx nx g @nrwl/react-native:lib your-lib-name +nx g @nrwl/react-native:lib your-lib-name ``` ### Generating Components @@ -68,20 +68,78 @@ npx nx g @nrwl/react-native:lib your-lib-name To generate a new component inside library run: ```shell -npx nx g @nrwl/react-native:component your-component-name --project=your-lib-name --export +nx g @nrwl/react-native:component your-component-name --project=your-lib-name --export ``` Replace `your-lib-name` with the app's name as defined in your `tsconfig.base.json` file or the `name` property of your `package.json` -## Using React Native +### Upgrade React Native -- [run-ios](/packages/react-native/executors/run-ios) - Builds your app and starts it on iOS simulator or device -- [run-android](/packages/react-native/executors/run-android) - Builds your app and starts it on a connected Android emulator or device -- [build-android](/packages/react-native/executors/build-android) - Release Build for Android -- [start](/packages/react-native/executors/start) - Starts the server that communicates with connected devices -- [bundle](/packages/react-native/executors/bundle) - Builds the JavaScript bundle for offline use -- [sync-deps](/packages/react-native/executors/sync-deps) - Syncs dependencies to package.json (required for autolinking) -- [ensure-symlink](/packages/react-native/executors/ensure-symlink) - Ensure workspace node_modules is symlink under app's node_modules folder +The Nx CLI provides the [`migrate` command](/core-features/automate-updating-dependencies) to help you stay up to date with the latest version of Nx. + +#### Use upgrade-native Generator + +To upgrade native iOS and Android code to latest, you can use the [upgrade-native](/packages/react-native/generators/upgrade-native) generator: + +```shell +nx generate @nrwl/react-native:upgrade-native +``` + +This is a command that will replace the iOS and Android native code folder entirely. + +#### Upgrade Manually + +You can also upgrade React Native iOS and Android code using the [rn-diff-purge](https://react-native-community.github.io/upgrade-helper/) project. + +### Start Metro Server + +To start the server that communicates with connected devices: + +```shell +nx start +``` + +### Run iOS + +To build your app and start it on iOS simulator or device: + +```shell +nx run-ios +``` + +### Run Android + +To build your app and start it on a connected Android emulator or device: + +```shell +nx run-android +``` + +### Build iOS + +To build an iOS app: + +```shell +nx build-ios +``` + +The build artifacts will be located under `/ios/build`. + +You can specify the build folder by setting the `buildFolder` option: + +```shell +nx build ios --buildFolder="./build" +``` + +### Build Android + +To build an Android app, run: + +```shell +nx build-android +``` + +The build artifacts will be located under `/android/app/build`. ## More Documentation diff --git a/docs/generated/packages/react-native/executors/build-android.json b/docs/generated/packages/react-native/executors/build-android.json index 3be3792e0e..a3589d9e8f 100644 --- a/docs/generated/packages/react-native/executors/build-android.json +++ b/docs/generated/packages/react-native/executors/build-android.json @@ -10,23 +10,87 @@ "title": "Release Build for Android", "description": "Build target options for Android.", "type": "object", + "presets": [ + { + "name": "Build Android for current device architecture", + "keys": ["activeArchOnly"] + }, + { "name": "Build Android without metro cache", "keys": ["resetCache"] }, + { "name": "Build Android with specific tasks", "keys": ["tasks"] }, + { "name": "Build Android with a specific mode", "keys": ["mode"] } + ], "properties": { "apk": { "type": "boolean", - "description": "Generate apk file(s) rather than a bundle (`.aab`)." + "description": "Generate apk file(s) rather than a bundle (`.aab`).", + "x-deprecated": "Use `tasks` option instead, e.g. `tasks=['bundleRelease']` to generate aab, `tasks=['assembleDebug']` to generate apk. Will be removed in Nx 17." }, "debug": { "type": "boolean", - "description": "Generate a debug build instead of a release build." + "description": "Generate a debug build instead of a release build.", + "x-deprecated": "Use `mode` option instead, e.g. `mode='debug'`. Deprecated from @react-native-community/cli. Will be removed in Nx 17." }, "gradleTask": { "type": "string", - "description": "Override default gradle task incase of multi build variants" + "description": "Override default gradle task incase of multi build variants", + "x-deprecated": "Use `tasks` option instead, e.g. `tasks=['assembleDebug']`. Will be removed in Nx 17." + }, + "mode": { + "type": "string", + "description": "Specify your app's build variant", + "default": "debug", + "examples": ["debug", "release"], + "x-priority": "important" + }, + "packager": { + "type": "boolean", + "description": "Launch packager while building", + "default": true + }, + "port": { + "type": "number", + "description": "The port where the packager server is listening on.", + "default": 8081 + }, + "tasks": { + "type": "array", + "items": { "type": "string" }, + "description": "Run custom Gradle tasks. By default it's \"assembleDebug\". Will override passed mode and variant arguments.", + "examples": [ + "assembleDebug", + "assembleRelease", + "bundleDebug", + "bundleRelease", + "installDebug", + "installRelease" + ] + }, + "activeArchOnly": { + "type": "boolean", + "description": "Build native libraries only for the current device architecture for debug builds.", + "default": false + }, + "extraParams": { + "type": "string", + "description": "Custom params passed to gradle build command" + }, + "interactive": { + "type": "boolean", + "description": "Explicitly select build type and flavour to use before running a build" + }, + "sync": { + "type": "boolean", + "description": "Syncs npm dependencies to `package.json` (for React Native autolink).", + "default": true + }, + "resetCache": { + "type": "boolean", + "description": "Resets metro cache.", + "default": false } }, "required": [], - "examplesFile": "`project.json`:\n\n```json\n{\n \"name\": \"mobile\",\n //...\n \"targets\": {\n //...\n \"build-android\": {\n \"executor\": \"@nrwl/react-native:build-android\",\n \"outputs\": [\n \"{projectRoot}/build/outputs/bundle\",\n \"{projectRoot}/build/outputs/apk\"\n ],\n \"options\": {}\n }\n }\n}\n```\n\n```bash\nnx run mobile:build-android\n```\n\n## Examples\n\n{% tabs %}\n{% tab label=\"Build with custom gradleTask\" %}\nThe `gradleTask` option accepts any custom gradle task, such as `assembleDebug`, `assembleRelease`, `bundleDebug`, `bundleRelease`:\n\n```json\n \"build-android\": {\n \"executor\": \"@nrwl/react-native:build-android\",\n \"outputs\": [\n \"{projectRoot}/build/outputs/bundle\",\n \"{projectRoot}/build/outputs/apk\"\n ],\n \"options\": {\n \"gradleTask\": \"assembleDebug\"\n }\n }\n```\n\n{% /tab %}\n{% tab label=\"Create a build with apk format\" %}\n\nThe `apk` option allows you determine the format of android build. If set as true, it will create a build with `.apk` extension under apk folder; if set as false, it will create with `.aab` extension under bundle folder.\n\n```json\n \"build-android\": {\n \"executor\": \"@nrwl/react-native:build-android\",\n \"outputs\": [\n \"{projectRoot}/build/outputs/bundle\",\n \"{projectRoot}/build/outputs/apk\"\n ],\n \"options\": {\n \"apk\": true\n }\n }\n```\n\n{% /tab %}\n{% tab label=\"Build for debug/release\" %}\n\nIf set `debug` option as `true`, it will create a debug build; if set as `false`, it will create a release build.\n\n```json\n \"build-android\": {\n \"executor\": \"@nrwl/react-native:build-android\",\n \"outputs\": [\n \"{projectRoot}/build/outputs/bundle\",\n \"{projectRoot}/build/outputs/apk\"\n ],\n \"options\": {\n \"debug\": true\n }\n }\n```\n\n{% /tab %}\n{% /tabs %}\n\n---\n", - "presets": [] + "examplesFile": "`project.json`:\n\n```json\n{\n \"name\": \"mobile\",\n //...\n \"targets\": {\n //...\n \"build-android\": {\n \"executor\": \"@nrwl/react-native:build-android\",\n \"outputs\": [\n \"{projectRoot}/build/outputs/bundle\",\n \"{projectRoot}/build/outputs/apk\"\n ],\n \"options\": {}\n }\n }\n}\n```\n\n```bash\nnx run mobile:build-android\n```\n\n## Examples\n\n{% tabs %}\n{% tab label=\"Build with custom tasks\" %}\nThe `tasks` option accepts any custom gradle task, such as `assembleDebug`, `assembleRelease`, `bundleDebug`, `bundleRelease`, `installDebug`, `installRelease`.\nFor example, pass in `bundleRelease` or `bundleRelease` to tasks, it will create with `.aab` extension under bundle folder.\nPass in `assembleDebug` or `assembleRelease` to tasks, it will create a build with `.apk` extension under apk folder.\nPass in `installDebug` or `installRelease` to tasks, it will create a build with `.apk` extension and immediately install it on a running emulator or connected device.\n\n```json\n \"build-android\": {\n \"executor\": \"@nrwl/react-native:build-android\",\n \"outputs\": [\n \"{projectRoot}/build/outputs/bundle\",\n \"{projectRoot}/build/outputs/apk\"\n ],\n \"options\": {\n \"tasks\": [\"bundleRelease\"]\n }\n }\n```\n\n{% /tab %}\n{% tab label=\"Build for debug/release\" %}\n\nThe `mode` option allows you determine whether to build for debug/release apk.\n\n```json\n \"build-android\": {\n \"executor\": \"@nrwl/react-native:build-android\",\n \"outputs\": [\n \"{projectRoot}/build/outputs/bundle\",\n \"{projectRoot}/build/outputs/apk\"\n ],\n \"options\": {\n \"mode\": \"debug\"\n }\n }\n```\n\n{% /tab %}\n{% tab label=\"Build for current device architecture\" %}\n\nThe `activeArchOnly` option allows you to build native libraries only for the current device architecture for debug builds.\n\n```json\n \"build-android\": {\n \"executor\": \"@nrwl/react-native:build-android\",\n \"outputs\": [\n \"{projectRoot}/build/outputs/bundle\",\n \"{projectRoot}/build/outputs/apk\"\n ],\n \"options\": {\n \"activeArchOnly\": true\n }\n }\n```\n\n{% /tab %}\n{% /tabs %}\n\n---\n" }, "description": "Release Build for Android.", "aliases": [], diff --git a/docs/generated/packages/react-native/executors/build-ios.json b/docs/generated/packages/react-native/executors/build-ios.json new file mode 100644 index 0000000000..b946ec070e --- /dev/null +++ b/docs/generated/packages/react-native/executors/build-ios.json @@ -0,0 +1,106 @@ +{ + "name": "build-ios", + "implementation": "/packages/react-native/src/executors/build-ios/build-ios.impl.ts", + "schema": { + "$schema": "http://json-schema.org/schema", + "version": 2, + "cli": "nx", + "title": "React Native Build iOS executor", + "description": "Build iOS app.", + "type": "object", + "presets": [ + { "name": "Build iOS for a simulator", "keys": ["simulator"] }, + { "name": "Build iOS for a device", "keys": ["device"] }, + { "name": "Build iOS for a device with udid", "keys": ["udid"] }, + { + "name": "Run `pod install` before building iOS app", + "keys": ["install"] + } + ], + "properties": { + "simulator": { + "type": "string", + "description": "Explicitly set simulator to use. Optionally include iOS version between parenthesis at the end to match an exact version: \"iPhone 6 (10.0)\"", + "examples": [ + "iPhone 14", + "iPhone 13", + "iPhone 12", + "iPhone 11", + "iPhone X" + ], + "x-priority": "important" + }, + "mode": { + "type": "string", + "description": "Explicitly set the scheme configuration to use", + "default": "Debug", + "examples": ["Debug", "Release"], + "x-priority": "important" + }, + "schema": { + "type": "string", + "description": "Explicitly set Xcode scheme to use" + }, + "device": { + "type": "string", + "description": "Explicitly set device to use by name. The value is not required if you have a single device connected." + }, + "udid": { + "type": "string", + "description": "Explicitly set device to use by udid" + }, + "verbose": { + "type": "boolean", + "description": "Do not use xcbeautify or xcpretty even if installed" + }, + "port": { + "type": "number", + "description": "The port where the packager server is listening on.", + "default": 8081 + }, + "xcconfig": { + "type": "string", + "description": "Explicitly set xcconfig to use" + }, + "buildFolder": { + "type": "string", + "description": "Location for iOS build artifacts. Corresponds to Xcode's \"-derivedDataPath\". Relative to ios directory", + "default": "./build" + }, + "interactive": { + "type": "boolean", + "description": "Explicitly select which scheme and configuration to use before running a build" + }, + "extraParams": { + "type": "string", + "description": "Custom params that will be passed to xcodebuild command." + }, + "install": { + "type": "boolean", + "description": "Runs `pod install` for native modules before building iOS app." + }, + "sync": { + "type": "boolean", + "description": "Syncs npm dependencies to `package.json` (for React Native autolink).", + "default": true + }, + "resetCache": { + "type": "boolean", + "description": "Resets metro cache.", + "default": false + }, + "packager": { + "type": "boolean", + "description": "Launch packager while building", + "default": true + } + }, + "required": [], + "examplesFile": "`project.json`:\n\n```json\n{\n \"name\": \"mobile\",\n //...\n \"targets\": {\n //...\n \"build-ios\": {\n \"executor\": \"@nrwl/react-native:build-ios\",\n \"options\": {}\n }\n }\n}\n```\n\n```bash\nnx run mobile:build-ios\n```\n\n## Examples\n\n{% tabs %}\n{% tab label=\"Build the Debug/Release app\" %}\nThe `buildFolder` option allows to specify the location for ios build artifacts. It corresponds to Xcode's -derivedDataPath.\n\n```json\n \"build-ios\": {\n \"executor\": \"@nrwl/react-native:build-ios\",\n \"options\": {\n \"buildFolder\": \"dist/ios/build\"\n }\n }\n```\n\n{% /tab %}\n{% tab label=\"Build the Debug/Release app\" %}\nThe `mode` option allows to specify the xcode configuartion, such as `Debug` or `Release`.\n\n```json\n \"build-ios\": {\n \"executor\": \"@nrwl/react-native:build-ios\",\n \"options\": {\n \"mode\": \"Release\"\n }\n }\n```\n\n{% /tab %}\n{% tab label=\"Build for a simulator\" %}\nTo see all the available simulators, run command:\n\n```bash\nxcrun simctl list\n```\n\nThe `simulator` option allows you to launch your iOS app in a specific simulator:\n\n```json\n \"build-ios\": {\n \"executor\": \"@nrwl/react-native:build-ios\",\n \"options\": {\n \"simulator\": \"iPhone 14 Pro\"\n }\n }\n```\n\n{% /tab %}\n{% tab label=\"Build for a device\" %}\nThe `device` option allows you to launch your iOS app in a specific device.\n\nTo see all the available device, run command:\n\n```bash\nxcrun simctl list\n```\n\n```json\n \"build-ios\": {\n \"executor\": \"@nrwl/react-native:build-ios\",\n \"options\": {\n \"device\": \"deviceName\"\n }\n }\n```\n\n{% /tab %}\n{% tab label=\"Set Device by udid\" %}\nThe `udid` option allows you to explicitly set device to use by udid.\n\nTo see all the available simulators and devices with udid, run command:\n\n```bash\nxcrun simctl list\n```\n\n```json\n \"build-ios\": {\n \"executor\": \"@nrwl/react-native:build-ios\",\n \"options\": {\n \"udid\": \"device udid\"\n }\n }\n```\n\n{% /tab %}\n{% /tabs %}\n\n---\n" + }, + "description": "Build iOS app", + "aliases": [], + "hidden": false, + "path": "/packages/react-native/src/executors/build-ios/schema.json", + "type": "executor" +} diff --git a/docs/generated/packages/react-native/executors/bundle.json b/docs/generated/packages/react-native/executors/bundle.json index 3b7238b1d7..ab66fe2ace 100644 --- a/docs/generated/packages/react-native/executors/bundle.json +++ b/docs/generated/packages/react-native/executors/bundle.json @@ -10,6 +10,12 @@ "title": "Offline JS Bundle for React Native", "description": "JS Bundle target options.", "type": "object", + "presets": [ + { "name": "Bundle for a specific platform", "keys": ["platform"] }, + { "name": "Bundle a development build", "keys": ["dev"] }, + { "name": "Bundle to a specific output path", "keys": ["bundleOutput"] }, + { "name": "Bundle without global cache", "keys": ["resetCache"] } + ], "properties": { "entryFile": { "type": "string", @@ -68,13 +74,12 @@ }, "readGlobalCache": { "type": "boolean", - "description": "Removes cached files.", + "description": "Try to fetch transformed JS code from the global cache, if configured.", "default": false } }, "required": ["platform", "entryFile", "bundleOutput"], - "examplesFile": "`project.json`:\n\n```json\n{\n \"name\": \"mobile\",\n //...\n \"targets\": {\n //...\n \"bundle-ios\": {\n \"executor\": \"@nrwl/react-native:bundle\",\n \"outputs\": [\"{projectRoot}/build\"],\n \"options\": {\n \"entryFile\": \"src/main.tsx\",\n \"platform\": \"ios\",\n \"bundleOutput\": \"dist/apps/mobile/ios/main.jsbundle\"\n }\n },\n \"bundle-android\": {\n \"executor\": \"@nrwl/react-native:bundle\",\n \"options\": {\n \"entryFile\": \"src/main.tsx\",\n \"platform\": \"android\",\n \"bundleOutput\": \"dist/apps/mobile/android/main.jsbundle\"\n }\n }\n }\n}\n```\n\n```bash\nnx run mobile:bundle-ios\nnx run mobile:bundle-android\n```\n\n## Examples\n\n{% tabs %}\n{% tab label=\"Bundle with sourcemap\" %}\nThe `sourcemapOutput` option allows you to specify the path of the source map relative to app folder:\n\n```json\n \"bundle-ios\": {\n \"executor\": \"@nrwl/react-native:bundle\",\n \"options\": {\n \"entryFile\": \"src/main.tsx\",\n \"platform\": \"ios\",\n \"bundleOutput\": \"dist/apps/mobile/ios/main.jsbundle\",\n \"sourcemapOutput\": \"../../dist/apps/mobile/ios/main.map\",\n }\n },\n \"bundle-android\": {\n \"executor\": \"@nrwl/react-native:bundle\",\n \"options\": {\n \"entryFile\": \"src/main.tsx\",\n \"platform\": \"android\",\n \"bundleOutput\": \"dist/apps/mobile/android/main.jsbundle\",\n \"sourcemapOutput\": \"../../dist/apps/mobile/android/main.map\",\n }\n }\n```\n\n{% /tab %}\n{% tab label=\"Create a dev/release bundle\" %}\n\nThe `dev` option determines whether to create a dev or release bundle. The default value is `true`, by setting it as `false`, warnings are disabled and the bundle is minified.\n\n```json\n \"bundle-ios\": {\n \"executor\": \"@nrwl/react-native:bundle\",\n \"options\": {\n \"entryFile\": \"src/main.tsx\",\n \"platform\": \"ios\",\n \"bundleOutput\": \"dist/apps/mobile/ios/main.jsbundle\",\n \"dev\": false\n }\n },\n \"bundle-android\": {\n \"executor\": \"@nrwl/react-native:bundle\",\n \"options\": {\n \"entryFile\": \"src/main.tsx\",\n \"platform\": \"android\",\n \"bundleOutput\": \"dist/apps/mobile/android/main.jsbundle\",\n \"dev\": false\n }\n }\n```\n\n{% /tab %}\n{% tab label=\"Create a minified bundle\" %}\n\nThe `minify` option allows you to create a minified bundle:\n\n```json\n \"bundle-ios\": {\n \"executor\": \"@nrwl/react-native:bundle\",\n \"options\": {\n \"entryFile\": \"src/main.tsx\",\n \"platform\": \"ios\",\n \"bundleOutput\": \"dist/apps/mobile/ios/main.jsbundle\",\n \"minify\": true\n }\n },\n \"bundle-android\": {\n \"executor\": \"@nrwl/react-native:bundle\",\n \"options\": {\n \"entryFile\": \"src/main.tsx\",\n \"platform\": \"android\",\n \"bundleOutput\": \"dist/apps/mobile/android/main.jsbundle\",\n \"minify\": true\n }\n }\n```\n\n{% /tab %}\n{% /tabs %}\n\n---\n", - "presets": [] + "examplesFile": "`project.json`:\n\n```json\n{\n \"name\": \"mobile\",\n //...\n \"targets\": {\n //...\n \"bundle-ios\": {\n \"executor\": \"@nrwl/react-native:bundle\",\n \"outputs\": [\"{projectRoot}/build\"],\n \"options\": {\n \"entryFile\": \"src/main.tsx\",\n \"platform\": \"ios\",\n \"bundleOutput\": \"dist/apps/mobile/ios/main.jsbundle\"\n }\n },\n \"bundle-android\": {\n \"executor\": \"@nrwl/react-native:bundle\",\n \"options\": {\n \"entryFile\": \"src/main.tsx\",\n \"platform\": \"android\",\n \"bundleOutput\": \"dist/apps/mobile/android/main.jsbundle\"\n }\n }\n }\n}\n```\n\n```bash\nnx run mobile:bundle-ios\nnx run mobile:bundle-android\n```\n\n## Examples\n\n{% tabs %}\n{% tab label=\"Bundle with sourcemap\" %}\nThe `sourcemapOutput` option allows you to specify the path of the source map relative to app folder:\n\n```json\n \"bundle-ios\": {\n \"executor\": \"@nrwl/react-native:bundle\",\n \"options\": {\n \"entryFile\": \"src/main.tsx\",\n \"platform\": \"ios\",\n \"bundleOutput\": \"dist/apps/mobile/ios/main.jsbundle\",\n \"sourcemapOutput\": \"../../dist/apps/mobile/ios/main.map\",\n }\n },\n \"bundle-android\": {\n \"executor\": \"@nrwl/react-native:bundle\",\n \"options\": {\n \"entryFile\": \"src/main.tsx\",\n \"platform\": \"android\",\n \"bundleOutput\": \"dist/apps/mobile/android/main.jsbundle\",\n \"sourcemapOutput\": \"../../dist/apps/mobile/android/main.map\",\n }\n }\n```\n\n{% /tab %}\n{% tab label=\"Create a dev/release bundle\" %}\n\nThe `dev` option determines whether to create a dev or release bundle. The default value is `true`, by setting it as `false`, warnings are disabled and the bundle is minified.\n\n```json\n \"bundle-ios\": {\n \"executor\": \"@nrwl/react-native:bundle\",\n \"options\": {\n \"entryFile\": \"src/main.tsx\",\n \"platform\": \"ios\",\n \"bundleOutput\": \"dist/apps/mobile/ios/main.jsbundle\",\n \"dev\": false\n }\n },\n \"bundle-android\": {\n \"executor\": \"@nrwl/react-native:bundle\",\n \"options\": {\n \"entryFile\": \"src/main.tsx\",\n \"platform\": \"android\",\n \"bundleOutput\": \"dist/apps/mobile/android/main.jsbundle\",\n \"dev\": false\n }\n }\n```\n\n{% /tab %}\n{% tab label=\"Create a minified bundle\" %}\n\nThe `minify` option allows you to create a minified bundle:\n\n```json\n \"bundle-ios\": {\n \"executor\": \"@nrwl/react-native:bundle\",\n \"options\": {\n \"entryFile\": \"src/main.tsx\",\n \"platform\": \"ios\",\n \"bundleOutput\": \"dist/apps/mobile/ios/main.jsbundle\",\n \"minify\": true\n }\n },\n \"bundle-android\": {\n \"executor\": \"@nrwl/react-native:bundle\",\n \"options\": {\n \"entryFile\": \"src/main.tsx\",\n \"platform\": \"android\",\n \"bundleOutput\": \"dist/apps/mobile/android/main.jsbundle\",\n \"minify\": true\n }\n }\n```\n\n{% /tab %}\n{% /tabs %}\n\n---\n" }, "description": "Builds the JavaScript bundle for offline use.", "aliases": [], diff --git a/docs/generated/packages/react-native/executors/pod-install.json b/docs/generated/packages/react-native/executors/pod-install.json new file mode 100644 index 0000000000..da7c4b7f32 --- /dev/null +++ b/docs/generated/packages/react-native/executors/pod-install.json @@ -0,0 +1,28 @@ +{ + "name": "pod-install", + "implementation": "/packages/react-native/src/executors/pod-install/pod-install.impl.ts", + "schema": { + "version": 2, + "outputCapture": "direct-nodejs", + "cli": "nx", + "$id": "NxReactNativePodInstall", + "$schema": "http://json-schema.org/schema", + "title": "Run Pod Install for React Native iOS Project", + "description": "Run `pod install` for React Native iOS Project.", + "type": "object", + "properties": { + "buildFolder": { + "description": "Location for iOS build artifacts. Corresponds to Xcode's \"-derivedDataPath\". Relative to ios directory", + "type": "string", + "default": "./build" + } + }, + "required": ["buildFolder"], + "presets": [] + }, + "description": "Run `pod install` in the `ios` directory.", + "aliases": [], + "hidden": false, + "path": "/packages/react-native/src/executors/pod-install/schema.json", + "type": "executor" +} diff --git a/docs/generated/packages/react-native/executors/run-android.json b/docs/generated/packages/react-native/executors/run-android.json index 3e94190db3..9eadf2db19 100644 --- a/docs/generated/packages/react-native/executors/run-android.json +++ b/docs/generated/packages/react-native/executors/run-android.json @@ -12,9 +12,14 @@ "type": "object", "presets": [ { - "name": "Run Android without cache", - "keys": ["variant", "sync", "port", "packager", "resetCache"] - } + "name": "Run Android for the current device architecture", + "keys": ["activeArchOnly"] + }, + { + "name": "Lists all available Android devices and simulators", + "keys": ["listDevices"] + }, + { "name": "Run Android without metro cache", "keys": ["resetCache"] } ], "properties": { "variant": { @@ -22,7 +27,12 @@ "description": "Specify your app's build variant (e.g. `debug`, `release`).", "default": "debug", "examples": ["debug", "release"], - "x-priority": "important" + "x-deprecated": "Deprecated from @react-native-community/cli, use mode instead, e.g. mode=debug. Will be remove in Nx 17." + }, + "jetifier": { + "type": "boolean", + "description": "Run Jetifier – the AndroidX transition tool. By default it runs before Gradle to ease working with libraries that don't support AndroidX yet.", + "x-deprecated": "Deprecated from @react-native-community/cli. Will be remove in Nx 17." }, "appId": { "type": "string", @@ -41,18 +51,25 @@ "type": "string", "description": "Builds your app and starts it on a specific device/simulator with the given device id (listed by running `adb devices` on the command line)." }, - "tasks": { + "listDevices": { + "type": "boolean", + "description": "Lists all available Android devices and simulators and let you choose one to run the app", + "default": false + }, + "binaryPath": { "type": "string", - "description": "Run custom Gradle tasks. If this argument is provided, then `--variant` option is ignored. Example: `yarn react-native run-android --tasks clean,installDebug`." + "description": "Path relative to project root where pre-built .apk binary lives." }, - "jetifier": { - "type": "boolean", - "description": "Run Jetifier – the AndroidX transition tool. By default it runs before Gradle to ease working with libraries that don't support AndroidX yet.", - "default": true + "mode": { + "type": "string", + "description": "Specify your app's build variant", + "default": "debug", + "examples": ["debug", "release"], + "x-priority": "important" }, - "sync": { + "packager": { "type": "boolean", - "description": "Syncs npm dependencies to `package.json` (for React Native autolink).", + "description": "Launch packager while building", "default": true }, "port": { @@ -60,32 +77,45 @@ "description": "The port where the packager server is listening on.", "default": 8081 }, - "terminal": { - "type": "string", - "description": "Launches the Metro Bundler in a new window using the specified terminal path." + "tasks": { + "type": "array", + "items": { "type": "string" }, + "description": "Run custom Gradle tasks. By default it's \"assembleDebug\". Will override passed mode and variant arguments.", + "examples": [ + "assembleDebug", + "assembleRelease", + "bundleDebug", + "bundleRelease", + "installDebug", + "installRelease" + ] }, - "packager": { + "activeArchOnly": { "type": "boolean", - "description": "Starts the packager server.", + "description": "Build native libraries only for the current device architecture for debug builds.", + "examples": ["x86_64", "arm64-v8a"], + "default": false + }, + "extraParams": { + "type": "string", + "description": "Custom params passed to gradle build command" + }, + "interactive": { + "type": "boolean", + "description": "Explicitly select build type and flavour to use before running a build" + }, + "sync": { + "type": "boolean", + "description": "Syncs npm dependencies to `package.json` (for React Native autolink).", "default": true }, "resetCache": { "type": "boolean", "description": "Resets metro cache.", "default": false - }, - "interactive": { - "type": "boolean", - "description": "Run packager server in interactive mode.", - "default": true - }, - "activeArchOnly": { - "type": "boolean", - "description": "Builds only for the active architecture (e.g. x86_64, arm64-v8a).", - "default": false } }, - "examplesFile": "`project.json`:\n\n```json\n{\n \"name\": \"mobile\",\n //...\n \"targets\": {\n //...\n \"run-android\": {\n \"executor\": \"@nrwl/react-native:run-android\",\n \"options\": {}\n }\n }\n}\n```\n\n```bash\nnx run mobile:run-android\n```\n\n## Examples\n\n{% tabs %}\n{% tab label=\"Run on a specific device/simulator\" %}\nTo see all the avaiable emulators, run command:\n\n```bash\nemulator -list-avds\n```\n\nThe `deviceId` option allows you to launch your android app in a specific device/simulator:\n\n```json\n \"run-android\": {\n \"executor\": \"@nrwl/react-native:run-android\",\n \"options\": {\n \"deviceId\": \"Pixel_5_API_30\"\n }\n }\n```\n\n{% /tab %}\n{% tab label=\"Run the debug/release app\" %}\nThe `variant` option allows to specify the build variant, such as `debug` or `release`.\n\n```json\n \"run-android\": {\n \"executor\": \"@nrwl/react-native:run-android\",\n \"options\": {\n \"variant\": \"release\"\n }\n }\n```\n\n{% /tab %}\n{% /tabs %}\n\n---\n" + "examplesFile": "`project.json`:\n\n```json\n{\n \"name\": \"mobile\",\n //...\n \"targets\": {\n //...\n \"run-android\": {\n \"executor\": \"@nrwl/react-native:run-android\",\n \"options\": {}\n }\n }\n}\n```\n\n```bash\nnx run mobile:run-android\n```\n\n## Examples\n\n{% tabs %}\n{% tab label=\"Run on a specific device/simulator\" %}\nTo see all the avaiable emulators, run command:\n\n```bash\nemulator -list-avds\n```\n\nThe `deviceId` option allows you to launch your android app in a specific device/simulator:\n\n```json\n \"run-android\": {\n \"executor\": \"@nrwl/react-native:run-android\",\n \"options\": {\n \"deviceId\": \"Pixel_5_API_30\"\n }\n }\n```\n\n{% /tab %}\n{% tab label=\"Run the debug/release app\" %}\nThe `mode` option allows to specify the build variant, such as `debug` or `release`.\n\n```json\n \"run-android\": {\n \"executor\": \"@nrwl/react-native:run-android\",\n \"options\": {\n \"mode\": \"release\"\n }\n }\n```\n\n{% /tab %}\n{% /tabs %}\n\n---\n" }, "description": "Runs Android application.", "aliases": [], diff --git a/docs/generated/packages/react-native/executors/run-ios.json b/docs/generated/packages/react-native/executors/run-ios.json index 035c0df6c1..fad11adbf5 100644 --- a/docs/generated/packages/react-native/executors/run-ios.json +++ b/docs/generated/packages/react-native/executors/run-ios.json @@ -11,16 +11,12 @@ "description": "Run iOS target options.", "type": "object", "presets": [ + { "name": "Run iOS on a simulator", "keys": ["simulator"] }, + { "name": "Run iOS on a device", "keys": ["device"] }, + { "name": "Run iOS on a device with udid", "keys": ["udid"] }, { - "name": "Run iOS without cache", - "keys": [ - "xcodeConfiguration", - "install", - "sync", - "port", - "packager", - "resetCache" - ] + "name": "Run `pod install` before building iOS app", + "keys": ["install"] } ], "properties": { @@ -29,16 +25,11 @@ "description": "Explicitly set the Xcode configuration to use.", "default": "Debug", "examples": ["Debug", "Release"], - "x-priority": "important" - }, - "scheme": { - "type": "string", - "description": "Explicitly set the Xcode scheme to use." + "x-deprecated": "Use `mode` instead. Deprecated from @react-native-community/cli. Will be removed in Nx 17." }, "simulator": { "type": "string", - "description": "Explicitly set simulator to use. Optionally include iOS version between parenthesis at the end to match an exact version: `iPhone X (12.1)`.", - "default": "iPhone 14", + "description": "Explicitly set simulator to use. Optionally include iOS version between parenthesis at the end to match an exact version: \"iPhone 6 (10.0)\"", "examples": [ "iPhone 14", "iPhone 13", @@ -48,10 +39,50 @@ ], "x-priority": "important" }, + "mode": { + "type": "string", + "description": "Explicitly set the scheme configuration to use", + "default": "Debug", + "examples": ["Debug", "Release"], + "x-priority": "important" + }, + "schema": { + "type": "string", + "description": "Explicitly set Xcode scheme to use" + }, "device": { "type": "string", - "description": "Explicitly set device to use by name. The value is not required if you have a single device connected.", - "x-priority": "important" + "description": "Explicitly set device to use by name. The value is not required if you have a single device connected." + }, + "udid": { + "type": "string", + "description": "Explicitly set device to use by udid" + }, + "verbose": { + "type": "boolean", + "description": "Do not use xcbeautify or xcpretty even if installed" + }, + "port": { + "type": "number", + "description": "The port where the packager server is listening on.", + "default": 8081 + }, + "xcconfig": { + "type": "string", + "description": "Explicitly set xcconfig to use" + }, + "buildFolder": { + "type": "string", + "description": "Location for iOS build artifacts. Corresponds to Xcode's \"-derivedDataPath\". Relative to ios directory.", + "buildFolder": "./build" + }, + "interactive": { + "type": "boolean", + "description": "Explicitly select which scheme and configuration to use before running a build" + }, + "extraParams": { + "type": "string", + "description": "Custom params that will be passed to xcodebuild command." }, "install": { "type": "boolean", @@ -60,22 +91,7 @@ }, "sync": { "type": "boolean", - "description": "Syncs npm dependencies to `package.json` (for React Native autolink). Always true when `--install` is used.", - "default": true, - "x-priority": "internal" - }, - "port": { - "type": "number", - "description": "The port where the packager server is listening on.", - "default": 8081 - }, - "terminal": { - "type": "string", - "description": "Launches the Metro Bundler in a new window using the specified terminal path." - }, - "packager": { - "type": "boolean", - "description": "Starts the packager server.", + "description": "Syncs npm dependencies to `package.json` (for React Native autolink).", "default": true }, "resetCache": { @@ -83,13 +99,17 @@ "description": "Resets metro cache.", "default": false }, - "interactive": { + "packager": { "type": "boolean", - "description": "Run packager server in interactive mode.", + "description": "Launch packager while building", "default": true + }, + "binaryPath": { + "type": "string", + "description": "Path relative to project root where pre-built .app binary lives." } }, - "examplesFile": "`project.json`:\n\n```json\n{\n \"name\": \"mobile\",\n //...\n \"targets\": {\n //...\n \"run-ios\": {\n \"executor\": \"@nrwl/react-native:run-ios\",\n \"options\": {}\n }\n }\n}\n```\n\n```bash\nnx run mobile:run-ios\n```\n\n## Examples\n\n{% tabs %}\n{% tab label=\"Run on a simulator\" %}\nTo see all the available simulators, run command:\n\n```bash\nxcrun simctl list\n```\n\nThe `simulator` option allows you to launch your iOS app in a specific simulator:\n\n```json\n \"run-ios\": {\n \"executor\": \"@nrwl/react-native:run-ios\",\n \"options\": {\n \"simulator\": \"iPhone 14 Pro\"\n }\n }\n```\n\n{% /tab %}\n{% tab label=\"Run on a device\" %}\nThe `device` option allows you to launch your iOS app in a specific device.\n\n```json\n \"run-ios\": {\n \"executor\": \"@nrwl/react-native:run-ios\",\n \"options\": {\n \"device\": \"deviceName\"\n }\n }\n```\n\n{% /tab %}\n{% tab label=\"Run the Debug/Release app\" %}\nThe `xcodeConfiguration` option allows to specify the xcode configuartion, such as `Debug` or `Release`.\n\n```json\n \"run-ios\": {\n \"executor\": \"@nrwl/react-native:run-ios\",\n \"options\": {\n \"xcodeConfiguration\": \"Release\"\n }\n }\n```\n\n{% /tab %}\n{% /tabs %}\n\n---\n" + "examplesFile": "`project.json`:\n\n```json\n{\n \"name\": \"mobile\",\n //...\n \"targets\": {\n //...\n \"run-ios\": {\n \"executor\": \"@nrwl/react-native:run-ios\",\n \"options\": {}\n }\n }\n}\n```\n\n```bash\nnx run mobile:run-ios\n```\n\n## Examples\n\n{% tabs %}\n{% tab label=\"Build the Debug/Release app\" %}\nThe `mode` option allows to specify the xcode configuartion schema, such as `Debug` or `Release`.\n\n```json\n \"run-ios\": {\n \"executor\": \"@nrwl/react-native:run-ios\",\n \"options\": {\n \"mode\": \"Release\"\n }\n }\n```\n\n{% /tab %}\n{% tab label=\"Run on a simulator\" %}\nTo see all the available simulators, run command:\n\n```bash\nxcrun simctl list\n```\n\nThe `simulator` option allows you to launch your iOS app in a specific simulator:\n\n```json\n \"run-ios\": {\n \"executor\": \"@nrwl/react-native:run-ios\",\n \"options\": {\n \"simulator\": \"iPhone 14 Pro\"\n }\n }\n```\n\n{% /tab %}\n{% tab label=\"Run on a device\" %}\nThe `device` option allows you to launch your iOS app in a specific device.\n\nTo see all the available devices, run command:\n\n```bash\nxcrun simctl list\n```\n\n```json\n \"run-ios\": {\n \"executor\": \"@nrwl/react-native:run-ios\",\n \"options\": {\n \"device\": \"deviceName\"\n }\n }\n```\n\n{% /tab %}\n{% tab label=\"Set Device by udid\" %}\nThe `udid` option allows you to explicitly set device to use by udid.\n\nTo see all the available simulators and devices with udid, run command:\n\n```bash\nxcrun simctl list\n```\n\n```json\n \"run-ios\": {\n \"executor\": \"@nrwl/react-native:run-ios\",\n \"options\": {\n \"udid\": \"device udid\"\n }\n }\n```\n\n{% /tab %}\n{% /tabs %}\n\n---\n" }, "description": "Runs iOS application.", "aliases": [], diff --git a/docs/shared/packages/react-native/react-native-plugin.md b/docs/shared/packages/react-native/react-native-plugin.md index a183575fcf..4c58fdabd4 100644 --- a/docs/shared/packages/react-native/react-native-plugin.md +++ b/docs/shared/packages/react-native/react-native-plugin.md @@ -52,7 +52,7 @@ yarn add --dev @nrwl/react-native To create additional React Native apps run: ```shell -nx g @nrwl/react-native:app your-app-name +nx g @nrwl/react-native:app ``` ### Generating Libraries @@ -60,7 +60,7 @@ nx g @nrwl/react-native:app your-app-name To generate a new library run: ```shell -npx nx g @nrwl/react-native:lib your-lib-name +nx g @nrwl/react-native:lib your-lib-name ``` ### Generating Components @@ -68,20 +68,78 @@ npx nx g @nrwl/react-native:lib your-lib-name To generate a new component inside library run: ```shell -npx nx g @nrwl/react-native:component your-component-name --project=your-lib-name --export +nx g @nrwl/react-native:component your-component-name --project=your-lib-name --export ``` Replace `your-lib-name` with the app's name as defined in your `tsconfig.base.json` file or the `name` property of your `package.json` -## Using React Native +### Upgrade React Native -- [run-ios](/packages/react-native/executors/run-ios) - Builds your app and starts it on iOS simulator or device -- [run-android](/packages/react-native/executors/run-android) - Builds your app and starts it on a connected Android emulator or device -- [build-android](/packages/react-native/executors/build-android) - Release Build for Android -- [start](/packages/react-native/executors/start) - Starts the server that communicates with connected devices -- [bundle](/packages/react-native/executors/bundle) - Builds the JavaScript bundle for offline use -- [sync-deps](/packages/react-native/executors/sync-deps) - Syncs dependencies to package.json (required for autolinking) -- [ensure-symlink](/packages/react-native/executors/ensure-symlink) - Ensure workspace node_modules is symlink under app's node_modules folder +The Nx CLI provides the [`migrate` command](/core-features/automate-updating-dependencies) to help you stay up to date with the latest version of Nx. + +#### Use upgrade-native Generator + +To upgrade native iOS and Android code to latest, you can use the [upgrade-native](/packages/react-native/generators/upgrade-native) generator: + +```shell +nx generate @nrwl/react-native:upgrade-native +``` + +This is a command that will replace the iOS and Android native code folder entirely. + +#### Upgrade Manually + +You can also upgrade React Native iOS and Android code using the [rn-diff-purge](https://react-native-community.github.io/upgrade-helper/) project. + +### Start Metro Server + +To start the server that communicates with connected devices: + +```shell +nx start +``` + +### Run iOS + +To build your app and start it on iOS simulator or device: + +```shell +nx run-ios +``` + +### Run Android + +To build your app and start it on a connected Android emulator or device: + +```shell +nx run-android +``` + +### Build iOS + +To build an iOS app: + +```shell +nx build-ios +``` + +The build artifacts will be located under `/ios/build`. + +You can specify the build folder by setting the `buildFolder` option: + +```shell +nx build ios --buildFolder="./build" +``` + +### Build Android + +To build an Android app, run: + +```shell +nx build-android +``` + +The build artifacts will be located under `/android/app/build`. ## More Documentation diff --git a/e2e/react-native/src/react-native.test.ts b/e2e/react-native/src/react-native.test.ts index bed990bcd2..e809176861 100644 --- a/e2e/react-native/src/react-native.test.ts +++ b/e2e/react-native/src/react-native.test.ts @@ -2,13 +2,18 @@ import { checkFilesExist, cleanupProject, expectTestsPass, + isOSX, + killPorts, newProject, + promisifiedTreeKill, readJson, runCLI, runCLIAsync, + runCommandUntil, uniq, updateFile, } from '@nrwl/e2e/utils'; +import { ChildProcess } from 'child_process'; import { join } from 'path'; describe('react native', () => { @@ -27,7 +32,7 @@ describe('react native', () => { }); afterAll(() => cleanupProject()); - it('should test, create ios and android JS bundles', async () => { + it('should test and lint', async () => { const componentName = uniq('component'); runCLI( `generate @nrwl/react-native:component ${componentName} --project=${libName} --export --no-interactive` @@ -46,7 +51,9 @@ describe('react native', () => { const libLintResults = await runCLIAsync(`lint ${libName}`); expect(libLintResults.combinedOutput).toContain('All files pass linting.'); + }); + it('should bundle-ios', async () => { const iosBundleResult = await runCLIAsync( `bundle-ios ${appName} --sourcemapOutput=../../dist/apps/${appName}/ios/main.map` ); @@ -57,7 +64,9 @@ describe('react native', () => { checkFilesExist(`dist/apps/${appName}/ios/main.jsbundle`); checkFilesExist(`dist/apps/${appName}/ios/main.map`); }).not.toThrow(); + }); + it('should bundle-android', async () => { const androidBundleResult = await runCLIAsync( `bundle-android ${appName} --sourcemapOutput=../../dist/apps/${appName}/android/main.map` ); @@ -68,7 +77,45 @@ describe('react native', () => { checkFilesExist(`dist/apps/${appName}/android/main.jsbundle`); checkFilesExist(`dist/apps/${appName}/android/main.map`); }).not.toThrow(); - }, 1000000); + }); + + it('should start', async () => { + let process: ChildProcess; + const port = 8081; + + try { + process = await runCommandUntil( + `start ${appName} --interactive=false --port=${port}`, + (output) => { + return ( + output.includes(`Packager is ready at http://localhost::${port}`) || + output.includes('Starting JS server...') + ); + } + ); + } catch (err) { + console.error(err); + } + + // port and process cleanup + try { + if (process && process.pid) { + await promisifiedTreeKill(process.pid, 'SIGKILL'); + await killPorts(port); + } + } catch (err) { + expect(err).toBeFalsy(); + } + }); + + if (isOSX()) { + it('should pod install', async () => { + expect(async () => { + await runCLIAsync(`pod-install ${appName}`); + checkFilesExist(`apps/${appName}/ios/Podfile.lock`); + }).not.toThrow(); + }); + } it('should create storybook with application', async () => { runCLI( diff --git a/packages/expo/src/utils/symlink-task.ts b/packages/expo/src/utils/symlink-task.ts index 47662c59e5..bc527097d2 100644 --- a/packages/expo/src/utils/symlink-task.ts +++ b/packages/expo/src/utils/symlink-task.ts @@ -3,13 +3,13 @@ import * as chalk from 'chalk'; import { GeneratorCallback, logger } from '@nrwl/devkit'; export function runSymlink( - worksapceRoot: string, + workspaceRoot: string, projectRoot: string ): GeneratorCallback { return () => { logger.info(`creating symlinks for ${chalk.bold(projectRoot)}`); try { - ensureNodeModulesSymlink(worksapceRoot, projectRoot); + ensureNodeModulesSymlink(workspaceRoot, projectRoot); } catch { throw new Error( `Failed to create symlinks for ${chalk.bold(projectRoot)}` diff --git a/packages/react-native/docs/build-android-examples.md b/packages/react-native/docs/build-android-examples.md index 464236bd25..4607fa70f1 100644 --- a/packages/react-native/docs/build-android-examples.md +++ b/packages/react-native/docs/build-android-examples.md @@ -25,8 +25,11 @@ nx run mobile:build-android ## Examples {% tabs %} -{% tab label="Build with custom gradleTask" %} -The `gradleTask` option accepts any custom gradle task, such as `assembleDebug`, `assembleRelease`, `bundleDebug`, `bundleRelease`: +{% tab label="Build with custom tasks" %} +The `tasks` option accepts any custom gradle task, such as `assembleDebug`, `assembleRelease`, `bundleDebug`, `bundleRelease`, `installDebug`, `installRelease`. +For example, pass in `bundleRelease` or `bundleRelease` to tasks, it will create with `.aab` extension under bundle folder. +Pass in `assembleDebug` or `assembleRelease` to tasks, it will create a build with `.apk` extension under apk folder. +Pass in `installDebug` or `installRelease` to tasks, it will create a build with `.apk` extension and immediately install it on a running emulator or connected device. ```json "build-android": { @@ -36,25 +39,7 @@ The `gradleTask` option accepts any custom gradle task, such as `assembleDebug`, "{projectRoot}/build/outputs/apk" ], "options": { - "gradleTask": "assembleDebug" - } - } -``` - -{% /tab %} -{% tab label="Create a build with apk format" %} - -The `apk` option allows you determine the format of android build. If set as true, it will create a build with `.apk` extension under apk folder; if set as false, it will create with `.aab` extension under bundle folder. - -```json - "build-android": { - "executor": "@nrwl/react-native:build-android", - "outputs": [ - "{projectRoot}/build/outputs/bundle", - "{projectRoot}/build/outputs/apk" - ], - "options": { - "apk": true + "tasks": ["bundleRelease"] } } ``` @@ -62,7 +47,7 @@ The `apk` option allows you determine the format of android build. If set as tru {% /tab %} {% tab label="Build for debug/release" %} -If set `debug` option as `true`, it will create a debug build; if set as `false`, it will create a release build. +The `mode` option allows you determine whether to build for debug/release apk. ```json "build-android": { @@ -72,7 +57,25 @@ If set `debug` option as `true`, it will create a debug build; if set as `false` "{projectRoot}/build/outputs/apk" ], "options": { - "debug": true + "mode": "debug" + } + } +``` + +{% /tab %} +{% tab label="Build for current device architecture" %} + +The `activeArchOnly` option allows you to build native libraries only for the current device architecture for debug builds. + +```json + "build-android": { + "executor": "@nrwl/react-native:build-android", + "outputs": [ + "{projectRoot}/build/outputs/bundle", + "{projectRoot}/build/outputs/apk" + ], + "options": { + "activeArchOnly": true } } ``` diff --git a/packages/react-native/docs/build-ios-examples.md b/packages/react-native/docs/build-ios-examples.md new file mode 100644 index 0000000000..a2c4004df1 --- /dev/null +++ b/packages/react-native/docs/build-ios-examples.md @@ -0,0 +1,109 @@ +`project.json`: + +```json +{ + "name": "mobile", + //... + "targets": { + //... + "build-ios": { + "executor": "@nrwl/react-native:build-ios", + "options": {} + } + } +} +``` + +```bash +nx run mobile:build-ios +``` + +## Examples + +{% tabs %} +{% tab label="Build the Debug/Release app" %} +The `buildFolder` option allows to specify the location for ios build artifacts. It corresponds to Xcode's -derivedDataPath. + +```json + "build-ios": { + "executor": "@nrwl/react-native:build-ios", + "options": { + "buildFolder": "dist/ios/build" + } + } +``` + +{% /tab %} +{% tab label="Build the Debug/Release app" %} +The `mode` option allows to specify the xcode configuartion, such as `Debug` or `Release`. + +```json + "build-ios": { + "executor": "@nrwl/react-native:build-ios", + "options": { + "mode": "Release" + } + } +``` + +{% /tab %} +{% tab label="Build for a simulator" %} +To see all the available simulators, run command: + +```bash +xcrun simctl list +``` + +The `simulator` option allows you to launch your iOS app in a specific simulator: + +```json + "build-ios": { + "executor": "@nrwl/react-native:build-ios", + "options": { + "simulator": "iPhone 14 Pro" + } + } +``` + +{% /tab %} +{% tab label="Build for a device" %} +The `device` option allows you to launch your iOS app in a specific device. + +To see all the available device, run command: + +```bash +xcrun simctl list +``` + +```json + "build-ios": { + "executor": "@nrwl/react-native:build-ios", + "options": { + "device": "deviceName" + } + } +``` + +{% /tab %} +{% tab label="Set Device by udid" %} +The `udid` option allows you to explicitly set device to use by udid. + +To see all the available simulators and devices with udid, run command: + +```bash +xcrun simctl list +``` + +```json + "build-ios": { + "executor": "@nrwl/react-native:build-ios", + "options": { + "udid": "device udid" + } + } +``` + +{% /tab %} +{% /tabs %} + +--- diff --git a/packages/react-native/docs/run-android-examples.md b/packages/react-native/docs/run-android-examples.md index b7d8d1ba2a..c963ee1319 100644 --- a/packages/react-native/docs/run-android-examples.md +++ b/packages/react-native/docs/run-android-examples.md @@ -41,13 +41,13 @@ The `deviceId` option allows you to launch your android app in a specific device {% /tab %} {% tab label="Run the debug/release app" %} -The `variant` option allows to specify the build variant, such as `debug` or `release`. +The `mode` option allows to specify the build variant, such as `debug` or `release`. ```json "run-android": { "executor": "@nrwl/react-native:run-android", "options": { - "variant": "release" + "mode": "release" } } ``` diff --git a/packages/react-native/docs/run-ios-examples.md b/packages/react-native/docs/run-ios-examples.md index 7b159f34a9..3a6ea25103 100644 --- a/packages/react-native/docs/run-ios-examples.md +++ b/packages/react-native/docs/run-ios-examples.md @@ -21,6 +21,19 @@ nx run mobile:run-ios ## Examples {% tabs %} +{% tab label="Build the Debug/Release app" %} +The `mode` option allows to specify the xcode configuartion schema, such as `Debug` or `Release`. + +```json + "run-ios": { + "executor": "@nrwl/react-native:run-ios", + "options": { + "mode": "Release" + } + } +``` + +{% /tab %} {% tab label="Run on a simulator" %} To see all the available simulators, run command: @@ -43,6 +56,12 @@ The `simulator` option allows you to launch your iOS app in a specific simulator {% tab label="Run on a device" %} The `device` option allows you to launch your iOS app in a specific device. +To see all the available devices, run command: + +```bash +xcrun simctl list +``` + ```json "run-ios": { "executor": "@nrwl/react-native:run-ios", @@ -53,14 +72,20 @@ The `device` option allows you to launch your iOS app in a specific device. ``` {% /tab %} -{% tab label="Run the Debug/Release app" %} -The `xcodeConfiguration` option allows to specify the xcode configuartion, such as `Debug` or `Release`. +{% tab label="Set Device by udid" %} +The `udid` option allows you to explicitly set device to use by udid. + +To see all the available simulators and devices with udid, run command: + +```bash +xcrun simctl list +``` ```json "run-ios": { "executor": "@nrwl/react-native:run-ios", "options": { - "xcodeConfiguration": "Release" + "udid": "device udid" } } ``` diff --git a/packages/react-native/executors.json b/packages/react-native/executors.json index 214e05a25a..f8dd98f444 100644 --- a/packages/react-native/executors.json +++ b/packages/react-native/executors.json @@ -20,6 +20,11 @@ "schema": "./src/executors/build-android/schema.json", "description": "Release Build for Android." }, + "build-ios": { + "implementation": "./src/executors/build-ios/build-ios.impl", + "schema": "./src/executors/build-ios/schema.json", + "description": "Build iOS app" + }, "start": { "implementation": "./src/executors/start/start.impl", "schema": "./src/executors/start/schema.json", @@ -39,6 +44,11 @@ "implementation": "./src/executors/storybook/storybook.impl", "schema": "./src/executors/storybook/schema.json", "description": "Serve React Native Storybook." + }, + "pod-install": { + "implementation": "./src/executors/pod-install/pod-install.impl", + "schema": "./src/executors/pod-install/schema.json", + "description": "Run `pod install` in the `ios` directory." } }, "builders": { @@ -62,6 +72,11 @@ "schema": "./src/executors/build-android/schema.json", "description": "Release Build for Android." }, + "build-ios": { + "implementation": "./src/executors/build-ios/compat", + "schema": "./src/executors/build-ios/schema.json", + "description": "Build iOS app" + }, "start": { "implementation": "./src/executors/start/compat", "schema": "./src/executors/start/schema.json", @@ -81,6 +96,11 @@ "implementation": "./src/executors/storybook/compat", "schema": "./src/executors/storybook/schema.json", "description": "Serve React Native Storybook." + }, + "pod-install": { + "implementation": "./src/executors/pod-install/compat", + "schema": "./src/executors/pod-install/schema.json", + "description": "Run `pod install` in the `ios` directory." } } } diff --git a/packages/react-native/migrations.json b/packages/react-native/migrations.json index 6da8c55c56..9cc708eb8b 100644 --- a/packages/react-native/migrations.json +++ b/packages/react-native/migrations.json @@ -71,6 +71,12 @@ "version": "15.0.0-beta.0", "description": "Adds babel.config.json to the hash of all tasks", "factory": "./src/migrations/update-15-0-0/add-babel-inputs" + }, + "add-build-ios-target": { + "cli": "nx", + "version": "15.9.1-beta.0", + "description": "Add target build-ios and pod-install for react native apps", + "factory": "./src/migrations/update-15-9-1/add-build-ios-target" } }, "packageJsonUpdates": { @@ -1261,6 +1267,19 @@ "alwaysAddToPackageJson": false } } + }, + "15.9.1": { + "version": "15.9.1-beta.0", + "packages": { + "@react-native-community/cli": { + "version": "10.2.1", + "alwaysAddToPackageJson": false + }, + "@react-native-community/cli-platform-ios": { + "version": "10.2.1", + "alwaysAddToPackageJson": false + } + } } } } diff --git a/packages/react-native/src/executors/build-android/build-android.impl.ts b/packages/react-native/src/executors/build-android/build-android.impl.ts index 7e2eba95ad..56be92996d 100644 --- a/packages/react-native/src/executors/build-android/build-android.impl.ts +++ b/packages/react-native/src/executors/build-android/build-android.impl.ts @@ -1,9 +1,16 @@ -import { ExecutorContext } from '@nrwl/devkit'; +import { ExecutorContext, names } from '@nrwl/devkit'; import { join } from 'path'; import { ensureNodeModulesSymlink } from '../../utils/ensure-node-modules-symlink'; -import { ChildProcess, spawn } from 'child_process'; -import { ReactNativeBuildOptions } from './schema'; +import { ChildProcess, fork } from 'child_process'; +import { ReactNativeBuildAndroidOptions } from './schema'; import { chmodAndroidGradlewFiles } from '../../utils/chmod-android-gradle-files'; +import { runCliStart } from '../start/start.impl'; +import { + displayNewlyAddedDepsMessage, + syncDeps, +} from '../sync-deps/sync-deps.impl'; +import { getCliOptions } from '../../utils/get-cli-options'; + export interface ReactNativeBuildOutput { success: boolean; } @@ -11,16 +18,39 @@ export interface ReactNativeBuildOutput { let childProcess: ChildProcess; export default async function* buildAndroidExecutor( - options: ReactNativeBuildOptions, + options: ReactNativeBuildAndroidOptions, context: ExecutorContext ): AsyncGenerator { const projectRoot = context.projectsConfigurations.projects[context.projectName].root; ensureNodeModulesSymlink(context.root, projectRoot); + if (options.sync) { + displayNewlyAddedDepsMessage( + context.projectName, + await syncDeps( + context.projectName, + projectRoot, + context.root, + context.projectGraph + ) + ); + } + chmodAndroidGradlewFiles(join(projectRoot, 'android')); try { - await runCliBuild(context.root, projectRoot, options); + const tasks = [runCliBuild(context.root, projectRoot, options)]; + if (options.packager && options.mode !== 'release') { + tasks.push( + runCliStart(context.root, projectRoot, { + port: options.port, + resetCache: options.resetCache, + interactive: options.interactive, + }) + ); + } + + await Promise.all(tasks); yield { success: true }; } finally { if (childProcess) { @@ -32,17 +62,19 @@ export default async function* buildAndroidExecutor( function runCliBuild( workspaceRoot: string, projectRoot: string, - options: ReactNativeBuildOptions + options: ReactNativeBuildAndroidOptions ) { return new Promise((resolve, reject) => { - const gradleCommand = getGradleCommand(options); - - childProcess = spawn( - process.platform === 'win32' ? 'gradlew.bat' : './gradlew', - [gradleCommand], + /** + * Call the react native cli with option `--no-packager` + * Not passing '--packager' due to cli will launch start command from the project root + */ + childProcess = fork( + join(workspaceRoot, './node_modules/react-native/cli.js'), + ['run-android', ...createBuildAndroidOptions(options), '--no-packager'], { - cwd: join(workspaceRoot, projectRoot, 'android'), - stdio: [0, 1, 2], + cwd: join(workspaceRoot, projectRoot), + env: { ...process.env, RCT_METRO_PORT: options.port.toString() }, } ); @@ -63,19 +95,14 @@ function runCliBuild( }); } -function getGradleCommand(options: ReactNativeBuildOptions) { - if (options?.gradleTask) { - return options.gradleTask; - } - if (options.apk) { - if (options.debug) { - return 'assembleDebug'; - } - return 'assembleRelease'; - } else { - if (options.debug) { - return 'bundleDebug'; - } - return 'bundleRelease'; - } +const nxOptions = ['sync', 'packager']; +const startOptions = ['port', 'resetCache']; +const deprecatedOptions = ['apk', 'debug', 'gradleTask']; + +function createBuildAndroidOptions(options: ReactNativeBuildAndroidOptions) { + return getCliOptions(options, [ + ...nxOptions, + ...startOptions, + ...deprecatedOptions, + ]); } diff --git a/packages/react-native/src/executors/build-android/schema.d.ts b/packages/react-native/src/executors/build-android/schema.d.ts index 4c7f574a07..24c8d29ce7 100644 --- a/packages/react-native/src/executors/build-android/schema.d.ts +++ b/packages/react-native/src/executors/build-android/schema.d.ts @@ -1,5 +1,31 @@ -export interface ReactNativeBuildOptions { +// options taken from https://github.com/react-native-community/cli/blob/main/packages/cli-platform-android/src/commands/buildAndroid/index.ts + +import { ReactNativeStartOptions } from '../start/schema'; + +export interface ReactNativeBuildAndroidOptions + extends ReactNativeStartOptions { + /** + * @deprecated, use tasks instead. e.g. tasks=['bundleRelease'] for aab, and tasks=['assembleRelease'] for apk. Will be removed in nx 17. + */ apk?: boolean; + /** + * @deprecated, use mode='debug' instead. Will be removed in nx 17. + */ debug?: boolean; + /** + * @deprecated, use tasks instead instead. Will be removed in nx 17. + */ gradleTask?: string; + + // react native options + mode: string; // default is debug + activeArchOnly: boolean; // default is false + port: number; // default is 8081 + tasks?: Array; + extraParams?: Array; + interactive?: boolean; + + // nx options + packager: boolean; // default is true + sync: boolean; } diff --git a/packages/react-native/src/executors/build-android/schema.json b/packages/react-native/src/executors/build-android/schema.json index 400ab878be..16284032ae 100644 --- a/packages/react-native/src/executors/build-android/schema.json +++ b/packages/react-native/src/executors/build-android/schema.json @@ -7,18 +7,94 @@ "title": "Release Build for Android", "description": "Build target options for Android.", "type": "object", + "presets": [ + { + "name": "Build Android for current device architecture", + "keys": ["activeArchOnly"] + }, + { + "name": "Build Android without metro cache", + "keys": ["resetCache"] + }, + { + "name": "Build Android with specific tasks", + "keys": ["tasks"] + }, + { + "name": "Build Android with a specific mode", + "keys": ["mode"] + } + ], "properties": { "apk": { "type": "boolean", - "description": "Generate apk file(s) rather than a bundle (`.aab`)." + "description": "Generate apk file(s) rather than a bundle (`.aab`).", + "x-deprecated": "Use `tasks` option instead, e.g. `tasks=['bundleRelease']` to generate aab, `tasks=['assembleDebug']` to generate apk. Will be removed in Nx 17." }, "debug": { "type": "boolean", - "description": "Generate a debug build instead of a release build." + "description": "Generate a debug build instead of a release build.", + "x-deprecated": "Use `mode` option instead, e.g. `mode='debug'`. Deprecated from @react-native-community/cli. Will be removed in Nx 17." }, "gradleTask": { "type": "string", - "description": "Override default gradle task incase of multi build variants" + "description": "Override default gradle task incase of multi build variants", + "x-deprecated": "Use `tasks` option instead, e.g. `tasks=['assembleDebug']`. Will be removed in Nx 17." + }, + "mode": { + "type": "string", + "description": "Specify your app's build variant", + "default": "debug", + "examples": ["debug", "release"], + "x-priority": "important" + }, + "packager": { + "type": "boolean", + "description": "Launch packager while building", + "default": true + }, + "port": { + "type": "number", + "description": "The port where the packager server is listening on.", + "default": 8081 + }, + "tasks": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Run custom Gradle tasks. By default it's \"assembleDebug\". Will override passed mode and variant arguments.", + "examples": [ + "assembleDebug", + "assembleRelease", + "bundleDebug", + "bundleRelease", + "installDebug", + "installRelease" + ] + }, + "activeArchOnly": { + "type": "boolean", + "description": "Build native libraries only for the current device architecture for debug builds.", + "default": false + }, + "extraParams": { + "type": "string", + "description": "Custom params passed to gradle build command" + }, + "interactive": { + "type": "boolean", + "description": "Explicitly select build type and flavour to use before running a build" + }, + "sync": { + "type": "boolean", + "description": "Syncs npm dependencies to `package.json` (for React Native autolink).", + "default": true + }, + "resetCache": { + "type": "boolean", + "description": "Resets metro cache.", + "default": false } }, "required": [], diff --git a/packages/react-native/src/executors/build-ios/build-ios.impl.ts b/packages/react-native/src/executors/build-ios/build-ios.impl.ts new file mode 100644 index 0000000000..f2a36eb002 --- /dev/null +++ b/packages/react-native/src/executors/build-ios/build-ios.impl.ts @@ -0,0 +1,118 @@ +import { ExecutorContext } from '@nrwl/devkit'; +import { join } from 'path'; +import { ChildProcess, fork } from 'child_process'; +import { platform } from 'os'; + +import { ensureNodeModulesSymlink } from '../../utils/ensure-node-modules-symlink'; +import { + displayNewlyAddedDepsMessage, + syncDeps, +} from '../sync-deps/sync-deps.impl'; +import { podInstall } from '../../utils/pod-install-task'; +import { ReactNativeBuildIosOptions } from './schema'; +import { runCliStart } from '../start/start.impl'; +import { getCliOptions } from '../../utils/get-cli-options'; + +export interface ReactNativeBuildIosOutput { + success: boolean; +} + +let childProcess: ChildProcess; + +export default async function* runIosExecutor( + options: ReactNativeBuildIosOptions, + context: ExecutorContext +): AsyncGenerator { + if (platform() !== 'darwin') { + throw new Error(`The run-ios build requires Mac to run`); + } + const projectRoot = + context.projectsConfigurations.projects[context.projectName].root; + ensureNodeModulesSymlink(context.root, projectRoot); + if (options.sync) { + displayNewlyAddedDepsMessage( + context.projectName, + await syncDeps( + context.projectName, + projectRoot, + context.root, + context.projectGraph + ) + ); + } + + if (options.install) { + await podInstall( + join(context.root, projectRoot, 'ios'), + options.buildFolder + ); + } + + try { + const tasks = [runCliBuildIOS(context.root, projectRoot, options)]; + if (options.packager && options.mode !== 'Release') { + tasks.push( + runCliStart(context.root, projectRoot, { + port: options.port, + resetCache: options.resetCache, + interactive: options.interactive, + }) + ); + } + + await Promise.all(tasks); + + yield { success: true }; + } finally { + if (childProcess) { + childProcess.kill(); + } + } +} + +function runCliBuildIOS( + workspaceRoot: string, + projectRoot: string, + options: ReactNativeBuildIosOptions +) { + return new Promise((resolve, reject) => { + /** + * Call the react native cli with option `--no-packager` + * Not passing '--packager' due to cli will launch start command from the project root + */ + childProcess = fork( + join(workspaceRoot, './node_modules/react-native/cli.js'), + ['run-ios', ...createBuildIOSOptions(options), '--no-packager'], + { + cwd: join(workspaceRoot, projectRoot), + env: { ...process.env, RCT_METRO_PORT: options.port.toString() }, + } + ); + + // Ensure the child process is killed when the parent exits + process.on('exit', () => childProcess.kill()); + process.on('SIGTERM', () => childProcess.kill()); + + childProcess.on('error', (err) => { + reject(err); + }); + childProcess.on('exit', (code) => { + if (code === 0) { + resolve(code); + } else { + reject(code); + } + }); + }); +} + +const nxOptions = ['sync', 'install', 'packager']; +const startOptions = ['port', 'resetCache']; + +function createBuildIOSOptions(options: ReactNativeBuildIosOptions) { + return getCliOptions( + options, + [...nxOptions, ...startOptions], + ['buildFolder'] + ); +} diff --git a/packages/react-native/src/executors/build-ios/compat.ts b/packages/react-native/src/executors/build-ios/compat.ts new file mode 100644 index 0000000000..3803011250 --- /dev/null +++ b/packages/react-native/src/executors/build-ios/compat.ts @@ -0,0 +1,5 @@ +import { convertNxExecutor } from '@nrwl/devkit'; + +import buildIosExecutor from './build-ios.impl'; + +export default convertNxExecutor(buildIosExecutor); diff --git a/packages/react-native/src/executors/build-ios/schema.d.ts b/packages/react-native/src/executors/build-ios/schema.d.ts new file mode 100644 index 0000000000..df4fac0ecc --- /dev/null +++ b/packages/react-native/src/executors/build-ios/schema.d.ts @@ -0,0 +1,22 @@ +// options from https://github.com/react-native-community/cli/blob/main/packages/cli-platform-ios/src/commands/buildIOS/index.ts + +import { ReactNativeStartOptions } from '../start/schema'; + +export interface ReactNativeBuildIosOptions extends ReactNativeStartOptions { + // react native options + simulator?: string; + mode: string; // default if 'Debug' + scheme?: string; + device?: string; + udid?: string; + verbose?: boolean; + port: number; // default is 8081 + xcconfig?: string; + buildFolder?: string; + extraParams?: string; + + // nx options + packager: boolean; // default is true + install: boolean; // default is true + sync: boolean; // default is true +} diff --git a/packages/react-native/src/executors/build-ios/schema.json b/packages/react-native/src/executors/build-ios/schema.json new file mode 100644 index 0000000000..0ee7fbb7e3 --- /dev/null +++ b/packages/react-native/src/executors/build-ios/schema.json @@ -0,0 +1,106 @@ +{ + "$schema": "http://json-schema.org/schema", + "version": 2, + "cli": "nx", + "title": "React Native Build iOS executor", + "description": "Build iOS app.", + "type": "object", + "presets": [ + { + "name": "Build iOS for a simulator", + "keys": ["simulator"] + }, + { + "name": "Build iOS for a device", + "keys": ["device"] + }, + { + "name": "Build iOS for a device with udid", + "keys": ["udid"] + }, + { + "name": "Run `pod install` before building iOS app", + "keys": ["install"] + } + ], + "properties": { + "simulator": { + "type": "string", + "description": "Explicitly set simulator to use. Optionally include iOS version between parenthesis at the end to match an exact version: \"iPhone 6 (10.0)\"", + "examples": [ + "iPhone 14", + "iPhone 13", + "iPhone 12", + "iPhone 11", + "iPhone X" + ], + "x-priority": "important" + }, + "mode": { + "type": "string", + "description": "Explicitly set the scheme configuration to use", + "default": "Debug", + "examples": ["Debug", "Release"], + "x-priority": "important" + }, + "schema": { + "type": "string", + "description": "Explicitly set Xcode scheme to use" + }, + "device": { + "type": "string", + "description": "Explicitly set device to use by name. The value is not required if you have a single device connected." + }, + "udid": { + "type": "string", + "description": "Explicitly set device to use by udid" + }, + "verbose": { + "type": "boolean", + "description": "Do not use xcbeautify or xcpretty even if installed" + }, + "port": { + "type": "number", + "description": "The port where the packager server is listening on.", + "default": 8081 + }, + "xcconfig": { + "type": "string", + "description": "Explicitly set xcconfig to use" + }, + "buildFolder": { + "type": "string", + "description": "Location for iOS build artifacts. Corresponds to Xcode's \"-derivedDataPath\". Relative to ios directory", + "default": "./build" + }, + "interactive": { + "type": "boolean", + "description": "Explicitly select which scheme and configuration to use before running a build" + }, + "extraParams": { + "type": "string", + "description": "Custom params that will be passed to xcodebuild command." + }, + "install": { + "type": "boolean", + "description": "Runs `pod install` for native modules before building iOS app." + }, + "sync": { + "type": "boolean", + "description": "Syncs npm dependencies to `package.json` (for React Native autolink).", + "default": true + }, + "resetCache": { + "type": "boolean", + "description": "Resets metro cache.", + "default": false + }, + "packager": { + "type": "boolean", + "description": "Launch packager while building", + "default": true + } + }, + "required": [], + "examplesFile": "../../../docs/build-ios-examples.md" +} diff --git a/packages/react-native/src/executors/bundle/schema.json b/packages/react-native/src/executors/bundle/schema.json index 23843b4cb1..3bf30ce819 100644 --- a/packages/react-native/src/executors/bundle/schema.json +++ b/packages/react-native/src/executors/bundle/schema.json @@ -7,6 +7,24 @@ "title": "Offline JS Bundle for React Native", "description": "JS Bundle target options.", "type": "object", + "presets": [ + { + "name": "Bundle for a specific platform", + "keys": ["platform"] + }, + { + "name": "Bundle a development build", + "keys": ["dev"] + }, + { + "name": "Bundle to a specific output path", + "keys": ["bundleOutput"] + }, + { + "name": "Bundle without global cache", + "keys": ["resetCache"] + } + ], "properties": { "entryFile": { "type": "string", @@ -65,7 +83,7 @@ }, "readGlobalCache": { "type": "boolean", - "description": "Removes cached files.", + "description": "Try to fetch transformed JS code from the global cache, if configured.", "default": false } }, diff --git a/packages/react-native/src/executors/pod-install/compat.ts b/packages/react-native/src/executors/pod-install/compat.ts new file mode 100644 index 0000000000..cc20f27361 --- /dev/null +++ b/packages/react-native/src/executors/pod-install/compat.ts @@ -0,0 +1,5 @@ +import { convertNxExecutor } from '@nrwl/devkit'; + +import podInstall from './pod-install.impl'; + +export default convertNxExecutor(podInstall); diff --git a/packages/react-native/src/executors/pod-install/pod-install.impl.ts b/packages/react-native/src/executors/pod-install/pod-install.impl.ts new file mode 100644 index 0000000000..ec595c75b7 --- /dev/null +++ b/packages/react-native/src/executors/pod-install/pod-install.impl.ts @@ -0,0 +1,21 @@ +import { join } from 'path'; +import { ExecutorContext } from '@nrwl/devkit'; + +import { runPodInstall } from '../../utils/pod-install-task'; +import { ReactNativePodInstallOptions } from './schema'; + +export interface ReactNativePodInstallOutput { + success: boolean; +} + +export default async function* podInstall( + options: ReactNativePodInstallOptions, + context: ExecutorContext +): AsyncGenerator { + const projectRoot = + context.projectsConfigurations.projects[context.projectName].root; + const iosDirectory = join(context.root, projectRoot, 'ios'); + await runPodInstall(iosDirectory, true, options.buildFolder)(); + + yield { success: true }; +} diff --git a/packages/react-native/src/executors/pod-install/schema.d.ts b/packages/react-native/src/executors/pod-install/schema.d.ts new file mode 100644 index 0000000000..11f7664df8 --- /dev/null +++ b/packages/react-native/src/executors/pod-install/schema.d.ts @@ -0,0 +1,3 @@ +export interface ReactNativePodInstallOptions { + buildFolder: string; +} diff --git a/packages/react-native/src/executors/pod-install/schema.json b/packages/react-native/src/executors/pod-install/schema.json new file mode 100644 index 0000000000..0cc954d982 --- /dev/null +++ b/packages/react-native/src/executors/pod-install/schema.json @@ -0,0 +1,18 @@ +{ + "version": 2, + "outputCapture": "direct-nodejs", + "cli": "nx", + "$id": "NxReactNativePodInstall", + "$schema": "http://json-schema.org/schema", + "title": "Run Pod Install for React Native iOS Project", + "description": "Run `pod install` for React Native iOS Project.", + "type": "object", + "properties": { + "buildFolder": { + "description": "Location for iOS build artifacts. Corresponds to Xcode's \"-derivedDataPath\". Relative to ios directory", + "type": "string", + "default": "./build" + } + }, + "required": ["buildFolder"] +} diff --git a/packages/react-native/src/executors/run-android/run-android.impl.ts b/packages/react-native/src/executors/run-android/run-android.impl.ts index 682de05d6b..006b656715 100644 --- a/packages/react-native/src/executors/run-android/run-android.impl.ts +++ b/packages/react-native/src/executors/run-android/run-android.impl.ts @@ -7,10 +7,10 @@ import { displayNewlyAddedDepsMessage, syncDeps, } from '../sync-deps/sync-deps.impl'; -import { chmodSync } from 'fs'; import { ReactNativeRunAndroidOptions } from './schema'; import { runCliStart } from '../start/start.impl'; import { chmodAndroidGradlewFiles } from '../../utils/chmod-android-gradle-files'; +import { getCliOptions } from '../../utils/get-cli-options'; export interface ReactNativeRunAndroidOutput { success: boolean; @@ -97,31 +97,14 @@ function runCliRunAndroid( }); } -const nxOrStartOptions = [ - 'sync', - 'install', - 'packager', - 'port', - 'resetCache', - 'interactive', -]; +const nxOptions = ['sync', 'packager']; +const startOptions = ['port', 'resetCache']; +const deprecatedOptions = ['variant', 'jetifier']; -function createRunAndroidOptions(options) { - return Object.keys(options).reduce((acc, k) => { - const v = options[k]; - if (k === 'mainActivity') { - acc.push(`--main-activity`, v); - } else if (k === 'jetifier') { - if (!v) { - acc.push(`--no-jetifier`); - } - } else if (k === 'activeArchOnly') { - if (v) { - acc.push(`--active-arch-only`); - } - } else if (v && !nxOrStartOptions.includes(k)) { - acc.push(`--${k}`, v); - } - return acc; - }, []); +function createRunAndroidOptions(options: ReactNativeRunAndroidOptions) { + return getCliOptions( + options, + [...nxOptions, ...startOptions, ...deprecatedOptions], + ['appId', 'appIdSuffix'] + ); } diff --git a/packages/react-native/src/executors/run-android/schema.d.ts b/packages/react-native/src/executors/run-android/schema.d.ts index 95213c0f8a..710d98a8e5 100644 --- a/packages/react-native/src/executors/run-android/schema.d.ts +++ b/packages/react-native/src/executors/run-android/schema.d.ts @@ -1,17 +1,24 @@ -// part of options from https://github.com/react-native-community/cli/blob/master/packages/platform-android/src/commands/runAndroid/index.ts#L314 -export interface ReactNativeRunAndroidOptions { +import { ReactNativeBuildAndroidOptions } from '../build-android/schema'; +import { ReactNativeStartOptions } from '../start/schema'; + +// part of options from https://github.com/react-native-community/cli/blob/main/packages/cli-platform-android/src/commands/runAndroid/index.ts +export interface ReactNativeRunAndroidOptions + extends ReactNativeBuildAndroidOptions { + /** + * @deprecated use mode instead + */ variant: string; + /** + * @deprecated no longer supported in react native cli + * https://github.com/react-native-community/cli/commit/7c003f2b1d9d80ec5c167614ba533a004272c685 + */ + jetifier: boolean; + + // react native options appId: string; appIdSuffix: string; mainActiviy: string; deviceId: string; - tasks?: string; - jetifier: boolean; - sync: boolean; - port: number; - terminal?: string; - packager: boolean; // default is true - resetCache: boolean; // default is false - interactive: boolean; // default is true - activeArchOnly?: boolean; + listDevices?: boolean; + binaryPath?: string; } diff --git a/packages/react-native/src/executors/run-android/schema.json b/packages/react-native/src/executors/run-android/schema.json index 2b5111bf09..256fed692b 100644 --- a/packages/react-native/src/executors/run-android/schema.json +++ b/packages/react-native/src/executors/run-android/schema.json @@ -9,8 +9,16 @@ "type": "object", "presets": [ { - "name": "Run Android without cache", - "keys": ["variant", "sync", "port", "packager", "resetCache"] + "name": "Run Android for the current device architecture", + "keys": ["activeArchOnly"] + }, + { + "name": "Lists all available Android devices and simulators", + "keys": ["listDevices"] + }, + { + "name": "Run Android without metro cache", + "keys": ["resetCache"] } ], "properties": { @@ -19,7 +27,12 @@ "description": "Specify your app's build variant (e.g. `debug`, `release`).", "default": "debug", "examples": ["debug", "release"], - "x-priority": "important" + "x-deprecated": "Deprecated from @react-native-community/cli, use mode instead, e.g. mode=debug. Will be remove in Nx 17." + }, + "jetifier": { + "type": "boolean", + "description": "Run Jetifier – the AndroidX transition tool. By default it runs before Gradle to ease working with libraries that don't support AndroidX yet.", + "x-deprecated": "Deprecated from @react-native-community/cli. Will be remove in Nx 17." }, "appId": { "type": "string", @@ -38,18 +51,25 @@ "type": "string", "description": "Builds your app and starts it on a specific device/simulator with the given device id (listed by running `adb devices` on the command line)." }, - "tasks": { + "listDevices": { + "type": "boolean", + "description": "Lists all available Android devices and simulators and let you choose one to run the app", + "default": false + }, + "binaryPath": { "type": "string", - "description": "Run custom Gradle tasks. If this argument is provided, then `--variant` option is ignored. Example: `yarn react-native run-android --tasks clean,installDebug`." + "description": "Path relative to project root where pre-built .apk binary lives." }, - "jetifier": { - "type": "boolean", - "description": "Run Jetifier – the AndroidX transition tool. By default it runs before Gradle to ease working with libraries that don't support AndroidX yet.", - "default": true + "mode": { + "type": "string", + "description": "Specify your app's build variant", + "default": "debug", + "examples": ["debug", "release"], + "x-priority": "important" }, - "sync": { + "packager": { "type": "boolean", - "description": "Syncs npm dependencies to `package.json` (for React Native autolink).", + "description": "Launch packager while building", "default": true }, "port": { @@ -57,29 +77,44 @@ "description": "The port where the packager server is listening on.", "default": 8081 }, - "terminal": { - "type": "string", - "description": "Launches the Metro Bundler in a new window using the specified terminal path." + "tasks": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Run custom Gradle tasks. By default it's \"assembleDebug\". Will override passed mode and variant arguments.", + "examples": [ + "assembleDebug", + "assembleRelease", + "bundleDebug", + "bundleRelease", + "installDebug", + "installRelease" + ] }, - "packager": { + "activeArchOnly": { "type": "boolean", - "description": "Starts the packager server.", + "description": "Build native libraries only for the current device architecture for debug builds.", + "examples": ["x86_64", "arm64-v8a"], + "default": false + }, + "extraParams": { + "type": "string", + "description": "Custom params passed to gradle build command" + }, + "interactive": { + "type": "boolean", + "description": "Explicitly select build type and flavour to use before running a build" + }, + "sync": { + "type": "boolean", + "description": "Syncs npm dependencies to `package.json` (for React Native autolink).", "default": true }, "resetCache": { "type": "boolean", "description": "Resets metro cache.", "default": false - }, - "interactive": { - "type": "boolean", - "description": "Run packager server in interactive mode.", - "default": true - }, - "activeArchOnly": { - "type": "boolean", - "description": "Builds only for the active architecture (e.g. x86_64, arm64-v8a).", - "default": false } }, "examplesFile": "../../../docs/run-android-examples.md" diff --git a/packages/react-native/src/executors/run-ios/run-ios.impl.ts b/packages/react-native/src/executors/run-ios/run-ios.impl.ts index 9543f7dab4..8150aa9505 100644 --- a/packages/react-native/src/executors/run-ios/run-ios.impl.ts +++ b/packages/react-native/src/executors/run-ios/run-ios.impl.ts @@ -1,4 +1,4 @@ -import { ExecutorContext } from '@nrwl/devkit'; +import { ExecutorContext, logger, names } from '@nrwl/devkit'; import { join } from 'path'; import { ChildProcess, fork } from 'child_process'; import { platform } from 'os'; @@ -11,6 +11,8 @@ import { import { podInstall } from '../../utils/pod-install-task'; import { ReactNativeRunIosOptions } from './schema'; import { runCliStart } from '../start/start.impl'; +import { getCliOptions } from '../../utils/get-cli-options'; +import { rmdirSync } from 'fs-extra'; export interface ReactNativeRunIosOutput { success: boolean; @@ -39,13 +41,17 @@ export default async function* runIosExecutor( ) ); } + if (options.install) { - await podInstall(join(context.root, projectRoot, 'ios')); + await podInstall( + join(context.root, projectRoot, 'ios'), + options.buildFolder + ); } try { const tasks = [runCliRunIOS(context.root, projectRoot, options)]; - if (options.packager && options.xcodeConfiguration !== 'Release') { + if (options.packager && options.mode !== 'Release') { tasks.push( runCliStart(context.root, projectRoot, { port: options.port, @@ -101,23 +107,14 @@ function runCliRunIOS( }); } -const nxOrStartOptions = [ - 'sync', - 'install', - 'packager', - 'port', - 'resetCache', - 'interactive', -]; +const nxOptions = ['sync', 'install', 'packager']; +const startOptions = ['port', 'resetCache']; +const deprecatedOptions = ['xcodeConfiguration']; -function createRunIOSOptions(options) { - return Object.keys(options).reduce((acc, k) => { - const v = options[k]; - if (k === 'xcodeConfiguration') { - acc.push('--configuration', v); - } else if (v && !nxOrStartOptions.includes(k)) { - acc.push(`--${k}`, options[k]); - } - return acc; - }, []); +function createRunIOSOptions(options: ReactNativeRunIosOptions) { + return getCliOptions( + options, + [...nxOptions, ...startOptions, ...deprecatedOptions], + ['buildFolder'] + ); } diff --git a/packages/react-native/src/executors/run-ios/schema.d.ts b/packages/react-native/src/executors/run-ios/schema.d.ts index 1b5c73ad15..934e49183e 100644 --- a/packages/react-native/src/executors/run-ios/schema.d.ts +++ b/packages/react-native/src/executors/run-ios/schema.d.ts @@ -1,14 +1,12 @@ -// part of options form https://github.com/react-native-community/cli/blob/master/packages/platform-ios/src/commands/runIOS/index.ts#L541 -export interface ReactNativeRunIosOptions { - xcodeConfiguration: string; - port: number; - scheme: string; - simulator: string; - device: string; - packager: boolean; // default is true - install?: boolean; - sync?: boolean; - terminal?: string; - resetCache: boolean; // default is false - interactive: boolean; // default is true +import { ReactNativeBuildIosOptions } from '../build-ios/schema'; +import { ReactNativeStartOptions } from '../start/schema'; + +// part of options form https://github.com/react-native-community/cli/blob/main/packages/cli-platform-ios/src/commands/runIOS/index.ts +export interface ReactNativeRunIosOptions extends ReactNativeBuildIosOptions { + /** + * @deprecated use mode instead, will be removed in nx 17. + */ + xcodeConfiguration?: string; + + binaryPath?: string; } diff --git a/packages/react-native/src/executors/run-ios/schema.json b/packages/react-native/src/executors/run-ios/schema.json index c841597f40..9e82e7dcc0 100644 --- a/packages/react-native/src/executors/run-ios/schema.json +++ b/packages/react-native/src/executors/run-ios/schema.json @@ -9,15 +9,20 @@ "type": "object", "presets": [ { - "name": "Run iOS without cache", - "keys": [ - "xcodeConfiguration", - "install", - "sync", - "port", - "packager", - "resetCache" - ] + "name": "Run iOS on a simulator", + "keys": ["simulator"] + }, + { + "name": "Run iOS on a device", + "keys": ["device"] + }, + { + "name": "Run iOS on a device with udid", + "keys": ["udid"] + }, + { + "name": "Run `pod install` before building iOS app", + "keys": ["install"] } ], "properties": { @@ -26,16 +31,11 @@ "description": "Explicitly set the Xcode configuration to use.", "default": "Debug", "examples": ["Debug", "Release"], - "x-priority": "important" - }, - "scheme": { - "type": "string", - "description": "Explicitly set the Xcode scheme to use." + "x-deprecated": "Use `mode` instead. Deprecated from @react-native-community/cli. Will be removed in Nx 17." }, "simulator": { "type": "string", - "description": "Explicitly set simulator to use. Optionally include iOS version between parenthesis at the end to match an exact version: `iPhone X (12.1)`.", - "default": "iPhone 14", + "description": "Explicitly set simulator to use. Optionally include iOS version between parenthesis at the end to match an exact version: \"iPhone 6 (10.0)\"", "examples": [ "iPhone 14", "iPhone 13", @@ -45,10 +45,50 @@ ], "x-priority": "important" }, + "mode": { + "type": "string", + "description": "Explicitly set the scheme configuration to use", + "default": "Debug", + "examples": ["Debug", "Release"], + "x-priority": "important" + }, + "schema": { + "type": "string", + "description": "Explicitly set Xcode scheme to use" + }, "device": { "type": "string", - "description": "Explicitly set device to use by name. The value is not required if you have a single device connected.", - "x-priority": "important" + "description": "Explicitly set device to use by name. The value is not required if you have a single device connected." + }, + "udid": { + "type": "string", + "description": "Explicitly set device to use by udid" + }, + "verbose": { + "type": "boolean", + "description": "Do not use xcbeautify or xcpretty even if installed" + }, + "port": { + "type": "number", + "description": "The port where the packager server is listening on.", + "default": 8081 + }, + "xcconfig": { + "type": "string", + "description": "Explicitly set xcconfig to use" + }, + "buildFolder": { + "type": "string", + "description": "Location for iOS build artifacts. Corresponds to Xcode's \"-derivedDataPath\". Relative to ios directory.", + "buildFolder": "./build" + }, + "interactive": { + "type": "boolean", + "description": "Explicitly select which scheme and configuration to use before running a build" + }, + "extraParams": { + "type": "string", + "description": "Custom params that will be passed to xcodebuild command." }, "install": { "type": "boolean", @@ -57,22 +97,7 @@ }, "sync": { "type": "boolean", - "description": "Syncs npm dependencies to `package.json` (for React Native autolink). Always true when `--install` is used.", - "default": true, - "x-priority": "internal" - }, - "port": { - "type": "number", - "description": "The port where the packager server is listening on.", - "default": 8081 - }, - "terminal": { - "type": "string", - "description": "Launches the Metro Bundler in a new window using the specified terminal path." - }, - "packager": { - "type": "boolean", - "description": "Starts the packager server.", + "description": "Syncs npm dependencies to `package.json` (for React Native autolink).", "default": true }, "resetCache": { @@ -80,10 +105,14 @@ "description": "Resets metro cache.", "default": false }, - "interactive": { + "packager": { "type": "boolean", - "description": "Run packager server in interactive mode.", + "description": "Launch packager while building", "default": true + }, + "binaryPath": { + "type": "string", + "description": "Path relative to project root where pre-built .app binary lives." } }, "examplesFile": "../../../docs/run-ios-examples.md" diff --git a/packages/react-native/src/executors/start/schema.d.ts b/packages/react-native/src/executors/start/schema.d.ts index 1681796682..6a50e82fcd 100644 --- a/packages/react-native/src/executors/start/schema.d.ts +++ b/packages/react-native/src/executors/start/schema.d.ts @@ -1,3 +1,5 @@ +// options from https://github.com/react-native-community/cli/blob/main/packages/cli-plugin-metro/src/commands/start/index.ts + export interface ReactNativeStartOptions { port: number; resetCache: boolean; // default is false diff --git a/packages/react-native/src/generators/application/lib/add-project.ts b/packages/react-native/src/generators/application/lib/add-project.ts index 52aaffdcf2..6e145b0387 100644 --- a/packages/react-native/src/generators/application/lib/add-project.ts +++ b/packages/react-native/src/generators/application/lib/add-project.ts @@ -44,6 +44,7 @@ function getTargets(options: NormalizedSchema) { architect['bundle-ios'] = { executor: '@nrwl/react-native:bundle', + outputs: ['{options.bundleOutput}'], options: { entryFile: options.entryFile, platform: 'ios', @@ -65,8 +66,20 @@ function getTargets(options: NormalizedSchema) { options: {}, }; + architect['build-ios'] = { + executor: '@nrwl/react-native:build-ios', + outputs: ['{projectRoot}/ios/build/Build'], + options: {}, + }; + + architect['pod-install'] = { + executor: '@nrwl/react-native:pod-install', + options: {}, + }; + architect['bundle-android'] = { executor: '@nrwl/react-native:bundle', + outputs: ['{options.bundleOutput}'], options: { entryFile: options.entryFile, platform: 'android', diff --git a/packages/react-native/src/migrations/update-15-9-1/add-build-ios-target.spec.ts b/packages/react-native/src/migrations/update-15-9-1/add-build-ios-target.spec.ts new file mode 100644 index 0000000000..3011e395cc --- /dev/null +++ b/packages/react-native/src/migrations/update-15-9-1/add-build-ios-target.spec.ts @@ -0,0 +1,36 @@ +import { addProjectConfiguration, getProjects, Tree } from '@nrwl/devkit'; +import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing'; +import update from './add-build-ios-target'; + +describe('add-build-ios-target', () => { + let tree: Tree; + + beforeEach(async () => { + tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' }); + addProjectConfiguration(tree, 'product', { + root: 'apps/product', + sourceRoot: 'apps/product/src', + targets: { + start: { + executor: '@nrwl/react-native:start', + }, + }, + }); + }); + + it(`should update project.json with target build-ios and pod-install`, async () => { + await update(tree); + + getProjects(tree).forEach((project) => { + expect(project.targets['build-ios']).toEqual({ + executor: '@nrwl/react-native:build-ios', + outputs: ['{projectRoot}/ios/build/Build'], + options: {}, + }); + expect(project.targets['pod-install']).toEqual({ + executor: '@nrwl/react-native:pod-install', + options: {}, + }); + }); + }); +}); diff --git a/packages/react-native/src/migrations/update-15-9-1/add-build-ios-target.ts b/packages/react-native/src/migrations/update-15-9-1/add-build-ios-target.ts new file mode 100644 index 0000000000..0ccaa673a4 --- /dev/null +++ b/packages/react-native/src/migrations/update-15-9-1/add-build-ios-target.ts @@ -0,0 +1,35 @@ +import { + Tree, + formatFiles, + getProjects, + updateProjectConfiguration, +} from '@nrwl/devkit'; + +/** + * Add build-ios target for react-native + */ +export default async function update(tree: Tree) { + const projects = getProjects(tree); + + for (const [name, config] of projects.entries()) { + if (config.targets?.['start']?.executor === '@nrwl/react-native:start') { + if (!config.targets['build-ios']) { + config.targets['build-ios'] = { + executor: '@nrwl/react-native:build-ios', + outputs: ['{projectRoot}/ios/build/Build'], + options: {}, + }; + } + if (!config.targets['pod-install']) { + config.targets['pod-install'] = { + executor: '@nrwl/react-native:pod-install', + options: {}, + }; + } + } + + updateProjectConfiguration(tree, name, config); + } + + await formatFiles(tree); +} diff --git a/packages/react-native/src/utils/get-cli-options.ts b/packages/react-native/src/utils/get-cli-options.ts new file mode 100644 index 0000000000..29b870896d --- /dev/null +++ b/packages/react-native/src/utils/get-cli-options.ts @@ -0,0 +1,30 @@ +import { names } from '@nrwl/devkit'; + +/** + * This function normalizes the options passed in to the Nx and returns an array of strings that can be passed to the React Native CLI. + * @param options Nx options + * @param optionKeysToIgnore Keys to ignore + * @param optionKeysInCamelName Keys that are in camel case. Most react native cli options are in kebab case, but some are in camel case. + * @returns options that can be passed to the React Native CLI + */ +export function getCliOptions( + options: T, + optionKeysToIgnore: string[] = [], + optionKeysInCamelName: string[] = [] +): string[] { + return Object.keys(options).reduce((acc, optionKey) => { + const optionValue = options[optionKey]; + if (!optionKeysToIgnore.includes(optionKey)) { + const cliKey = optionKeysInCamelName.includes(optionKey) + ? names(optionKey).propertyName + : names(optionKey).fileName; // cli uses kebab case as default + if (typeof optionValue === 'boolean' && optionValue) { + // no need to pass in the value when it is true, just the flag name + acc.push(`--${cliKey}`); + } else { + acc.push(`--${cliKey}`, optionValue); + } + } + return acc; + }, []); +} diff --git a/packages/react-native/src/utils/pod-install-task.ts b/packages/react-native/src/utils/pod-install-task.ts index fda361d92a..6fd0473944 100644 --- a/packages/react-native/src/utils/pod-install-task.ts +++ b/packages/react-native/src/utils/pod-install-task.ts @@ -2,6 +2,8 @@ import { execSync } from 'child_process'; import { platform } from 'os'; import * as chalk from 'chalk'; import { GeneratorCallback, logger } from '@nrwl/devkit'; +import { rmdirSync, existsSync } from 'fs-extra'; +import { join } from 'path'; const podInstallErrorMessage = ` Running ${chalk.bold('pod install')} failed, see above. @@ -21,7 +23,8 @@ ${chalk.bold('sudo xcode-select --switch /Applications/Xcode.app')} */ export function runPodInstall( iosDirectory: string, - install: boolean = true + install: boolean = true, + buildFolder?: string ): GeneratorCallback { return () => { if (platform() !== 'darwin') { @@ -36,17 +39,27 @@ export function runPodInstall( logger.info(`Running \`pod install\` from "${iosDirectory}"`); - return podInstall(iosDirectory); + return podInstall(iosDirectory, buildFolder); }; } -export function podInstall(iosDirectory: string): Promise { +export function podInstall( + iosDirectory: string, + buildFolder?: string +): Promise { return new Promise((resolve, reject) => { const result = execSync('pod install', { cwd: iosDirectory, }); logger.info(result.toString()); if (result.toString().includes('Pod installation complete')) { + // Remove build folder after pod install + if (buildFolder) { + buildFolder = join(iosDirectory, buildFolder); + if (existsSync(buildFolder)) { + rmdirSync(buildFolder, { recursive: true }); + } + } resolve(); } else { reject(new Error(podInstallErrorMessage)); diff --git a/packages/react-native/src/utils/symlink-task.ts b/packages/react-native/src/utils/symlink-task.ts index 47662c59e5..bc527097d2 100644 --- a/packages/react-native/src/utils/symlink-task.ts +++ b/packages/react-native/src/utils/symlink-task.ts @@ -3,13 +3,13 @@ import * as chalk from 'chalk'; import { GeneratorCallback, logger } from '@nrwl/devkit'; export function runSymlink( - worksapceRoot: string, + workspaceRoot: string, projectRoot: string ): GeneratorCallback { return () => { logger.info(`creating symlinks for ${chalk.bold(projectRoot)}`); try { - ensureNodeModulesSymlink(worksapceRoot, projectRoot); + ensureNodeModulesSymlink(workspaceRoot, projectRoot); } catch { throw new Error( `Failed to create symlinks for ${chalk.bold(projectRoot)}` diff --git a/packages/react-native/src/utils/versions.ts b/packages/react-native/src/utils/versions.ts index bd875c05ca..86a4a9c7d7 100644 --- a/packages/react-native/src/utils/versions.ts +++ b/packages/react-native/src/utils/versions.ts @@ -7,8 +7,8 @@ export const typesNodeVersion = '18.14.4'; export const metroVersion = '0.74.1'; -export const reactNativeCommunityCli = '10.2.0'; -export const reactNativeCommunityCliIos = '10.2.0'; +export const reactNativeCommunityCli = '10.2.1'; +export const reactNativeCommunityCliIos = '10.2.1'; export const reactNativeCommunityCliAndroid = '10.2.0'; export const reactVersion = '18.2.0';