feat(react-native): upgrade react-native to 0.72 (#17810)

This commit is contained in:
Emily Xiong 2023-07-17 13:07:46 -04:00 committed by GitHub
parent 6529be96cf
commit 699a20ca03
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
94 changed files with 1652 additions and 777 deletions

View File

@ -50,7 +50,7 @@ nx start my-app
To generate a new library run: To generate a new library run:
```shell ```shell
npx nx g @nx/react-native:lib your-lib-name npx nx g @nx/expo:lib your-lib-name
``` ```
### Generating Components ### Generating Components
@ -58,7 +58,7 @@ npx nx g @nx/react-native:lib your-lib-name
To generate a new component inside library run: To generate a new component inside library run:
```shell ```shell
npx nx g @nx/react-native:component your-component-name --project=your-lib-name --export npx nx g @nx/expo: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` 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`
@ -247,6 +247,28 @@ To check the details of your build status, run:
nx build-list <app-name> nx build-list <app-name>
``` ```
### Submit an EAS Build
EAS Submit is a hosted service for uploading and submitting your app binaries to the app stores. Since it's a hosted service, you can submit your app to both stores as long as you can run EAS CLI on your machine.
To submit an EAS build:
```shell
nx submit <app-name>
```
### Update an EAS Build
EAS Update is a hosted service that serves updates for projects using the `expo-updates` library.
EAS Update makes fixing small bugs and pushing quick fixes a snap in between app store submissions. It accomplishes this by allowing an end-user's app to swap out the non-native parts of their app (for example, JS, styling, and image changes) with a new update that contains bug fixes and other updates.
To update an EAS build:
```shell
nx update <app-name>
```
### Testing Projects ### Testing Projects
You can run unit tests with: You can run unit tests with:
@ -269,6 +291,8 @@ Below table is a map between expo commands and Nx commands:
| `expo install` | `nx install <app-name>` | | `expo install` | `nx install <app-name>` |
| `eas build` | `nx build <app-name>` | | `eas build` | `nx build <app-name>` |
| `eas build:list` | `nx build-list <app-name>` | | `eas build:list` | `nx build-list <app-name>` |
| `eas update` | `nx update <app-name>` |
| `eas submit` | `nx submit <app-name>` |
## More Documentation ## More Documentation

View File

@ -46,7 +46,8 @@
}, },
"output": { "output": {
"type": "string", "type": "string",
"description": "Output path for local build" "description": "Output path for local build",
"examples": ["../../dist/MyApp.tar.gz", "../../dist"]
}, },
"wait": { "wait": {
"type": "boolean", "type": "boolean",

View File

@ -42,6 +42,7 @@
"type": "boolean", "type": "boolean",
"description": "Syncs npm dependencies to package.json (for React Native autolink).", "description": "Syncs npm dependencies to package.json (for React Native autolink).",
"default": true, "default": true,
"x-deprecated": "Add sync-deps to dependsOn in project.json for this target instead",
"x-priority": "internal" "x-priority": "internal"
}, },
"port": { "port": {

View File

@ -88,6 +88,7 @@
"sync": { "sync": {
"type": "boolean", "type": "boolean",
"description": "Syncs npm dependencies to package.json (for React Native autolink).", "description": "Syncs npm dependencies to package.json (for React Native autolink).",
"x-deprecated": "Add sync-deps to dependsOn in project.json for this target instead",
"default": true "default": true
} }
}, },

View File

@ -45,7 +45,8 @@
"packager": { "packager": {
"type": "boolean", "type": "boolean",
"description": "Launch packager while building", "description": "Launch packager while building",
"default": true "default": true,
"x-deprecated": "Run `nx run <project>:start` instead. Will be removed in Nx 17."
}, },
"port": { "port": {
"type": "number", "type": "number",
@ -53,8 +54,10 @@
"default": 8081 "default": 8081
}, },
"tasks": { "tasks": {
"type": "array", "oneOf": [
"items": { "type": "string" }, { "type": "array", "items": { "type": "string" } },
{ "type": "string" }
],
"description": "Run custom Gradle tasks. By default it's \"assembleDebug\". Will override passed mode and variant arguments.", "description": "Run custom Gradle tasks. By default it's \"assembleDebug\". Will override passed mode and variant arguments.",
"examples": [ "examples": [
"assembleDebug", "assembleDebug",
@ -71,8 +74,12 @@
"default": false "default": false
}, },
"extraParams": { "extraParams": {
"type": "string", "oneOf": [
"description": "Custom params passed to gradle build command" { "type": "array", "items": { "type": "string" } },
{ "type": "string" }
],
"description": "Custom params passed to gradle build command",
"examples": ["-x lint -x test"]
}, },
"interactive": { "interactive": {
"type": "boolean", "type": "boolean",
@ -81,7 +88,8 @@
"sync": { "sync": {
"type": "boolean", "type": "boolean",
"description": "Syncs npm dependencies to `package.json` (for React Native autolink).", "description": "Syncs npm dependencies to `package.json` (for React Native autolink).",
"default": true "default": true,
"x-deprecated": "Add sync-deps to dependsOn instead"
}, },
"resetCache": { "resetCache": {
"type": "boolean", "type": "boolean",

View File

@ -11,11 +11,7 @@
"presets": [ "presets": [
{ "name": "Build iOS for a simulator", "keys": ["simulator"] }, { "name": "Build iOS for a simulator", "keys": ["simulator"] },
{ "name": "Build iOS for a device", "keys": ["device"] }, { "name": "Build iOS for a device", "keys": ["device"] },
{ "name": "Build iOS for a device with udid", "keys": ["udid"] }, { "name": "Build iOS for a device with udid", "keys": ["udid"] }
{
"name": "Run `pod install` before building iOS app",
"keys": ["install"]
}
], ],
"properties": { "properties": {
"simulator": { "simulator": {
@ -72,17 +68,22 @@
"description": "Explicitly select which scheme and configuration to use before running a build" "description": "Explicitly select which scheme and configuration to use before running a build"
}, },
"extraParams": { "extraParams": {
"type": "string", "oneOf": [
{ "type": "array", "items": { "type": "string" } },
{ "type": "string" }
],
"description": "Custom params that will be passed to xcodebuild command." "description": "Custom params that will be passed to xcodebuild command."
}, },
"install": { "install": {
"type": "boolean", "type": "boolean",
"description": "Runs `pod install` for native modules before building iOS app.", "description": "Runs `pod install` for native modules before building iOS app.",
"x-deprecated": "Add pod-install to dependsOn in project.json for this target instead",
"default": true "default": true
}, },
"sync": { "sync": {
"type": "boolean", "type": "boolean",
"description": "Syncs npm dependencies to `package.json` (for React Native autolink).", "description": "Syncs npm dependencies to `package.json` (for React Native autolink).",
"x-deprecated": "Add sync-deps to dependsOn in project.json for this target instead",
"default": true "default": true
}, },
"resetCache": { "resetCache": {
@ -93,7 +94,8 @@
"packager": { "packager": {
"type": "boolean", "type": "boolean",
"description": "Launch packager while building", "description": "Launch packager while building",
"default": true "default": true,
"x-deprecated": "Run `nx run <project>:start` instead. Will be removed in Nx 17."
} }
}, },
"required": [], "required": [],

View File

@ -12,9 +12,19 @@
"type": "object", "type": "object",
"properties": { "properties": {
"buildFolder": { "buildFolder": {
"description": "Location for iOS build artifacts. Corresponds to Xcode's \"-derivedDataPath\". Relative to ios directory", "description": "Location for iOS build artifacts. Corresponds to Xcode's \"-derivedDataPath\". Relative to ios directory.",
"type": "string", "type": "string",
"default": "./build" "default": "./build"
},
"repoUpdate": {
"description": "Force running `pod repo update` before install.",
"type": "boolean",
"default": false
},
"deployment": {
"description": "Disallow any changes to the Podfile or the Podfile.lock during installation.",
"type": "boolean",
"default": false
} }
}, },
"required": ["buildFolder"], "required": ["buildFolder"],

View File

@ -77,8 +77,10 @@
"default": 8081 "default": 8081
}, },
"tasks": { "tasks": {
"type": "array", "oneOf": [
"items": { "type": "string" }, { "type": "array", "items": { "type": "string" } },
{ "type": "string" }
],
"description": "Run custom Gradle tasks. By default it's \"assembleDebug\". Will override passed mode and variant arguments.", "description": "Run custom Gradle tasks. By default it's \"assembleDebug\". Will override passed mode and variant arguments.",
"examples": [ "examples": [
"assembleDebug", "assembleDebug",
@ -95,7 +97,10 @@
"default": false "default": false
}, },
"extraParams": { "extraParams": {
"type": "string", "oneOf": [
{ "type": "array", "items": { "type": "string" } },
{ "type": "string" }
],
"description": "Custom params passed to gradle build command" "description": "Custom params passed to gradle build command"
}, },
"interactive": { "interactive": {
@ -105,6 +110,7 @@
"sync": { "sync": {
"type": "boolean", "type": "boolean",
"description": "Syncs npm dependencies to `package.json` (for React Native autolink).", "description": "Syncs npm dependencies to `package.json` (for React Native autolink).",
"x-deprecated": "Add sync-deps to dependsOn for this target in project.json instead",
"default": true "default": true
}, },
"resetCache": { "resetCache": {

View File

@ -13,11 +13,7 @@
"presets": [ "presets": [
{ "name": "Run iOS on a simulator", "keys": ["simulator"] }, { "name": "Run iOS on a simulator", "keys": ["simulator"] },
{ "name": "Run iOS on a device", "keys": ["device"] }, { "name": "Run iOS on a device", "keys": ["device"] },
{ "name": "Run iOS on a device with udid", "keys": ["udid"] }, { "name": "Run iOS on a device with udid", "keys": ["udid"] }
{
"name": "Run `pod install` before building iOS app",
"keys": ["install"]
}
], ],
"properties": { "properties": {
"xcodeConfiguration": { "xcodeConfiguration": {
@ -81,17 +77,22 @@
"description": "Explicitly select which scheme and configuration to use before running a build" "description": "Explicitly select which scheme and configuration to use before running a build"
}, },
"extraParams": { "extraParams": {
"type": "string", "oneOf": [
{ "type": "array", "items": { "type": "string" } },
{ "type": "string" }
],
"description": "Custom params that will be passed to xcodebuild command." "description": "Custom params that will be passed to xcodebuild command."
}, },
"install": { "install": {
"type": "boolean", "type": "boolean",
"description": "Runs `pod install` for native modules before building iOS app.", "description": "Runs `pod install` for native modules before building iOS app.",
"default": true "default": true,
"x-deprecated": "Add `pod-install` to dependsOn for this target in project.json instead."
}, },
"sync": { "sync": {
"type": "boolean", "type": "boolean",
"description": "Syncs npm dependencies to `package.json` (for React Native autolink).", "description": "Syncs npm dependencies to `package.json` (for React Native autolink).",
"x-deprecated": "Add `sync-deps` to dependsOn for this target in project.json instead.",
"default": true "default": true
}, },
"resetCache": { "resetCache": {

View File

@ -50,7 +50,7 @@ nx start my-app
To generate a new library run: To generate a new library run:
```shell ```shell
npx nx g @nx/react-native:lib your-lib-name npx nx g @nx/expo:lib your-lib-name
``` ```
### Generating Components ### Generating Components
@ -58,7 +58,7 @@ npx nx g @nx/react-native:lib your-lib-name
To generate a new component inside library run: To generate a new component inside library run:
```shell ```shell
npx nx g @nx/react-native:component your-component-name --project=your-lib-name --export npx nx g @nx/expo: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` 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`
@ -247,6 +247,28 @@ To check the details of your build status, run:
nx build-list <app-name> nx build-list <app-name>
``` ```
### Submit an EAS Build
EAS Submit is a hosted service for uploading and submitting your app binaries to the app stores. Since it's a hosted service, you can submit your app to both stores as long as you can run EAS CLI on your machine.
To submit an EAS build:
```shell
nx submit <app-name>
```
### Update an EAS Build
EAS Update is a hosted service that serves updates for projects using the `expo-updates` library.
EAS Update makes fixing small bugs and pushing quick fixes a snap in between app store submissions. It accomplishes this by allowing an end-user's app to swap out the non-native parts of their app (for example, JS, styling, and image changes) with a new update that contains bug fixes and other updates.
To update an EAS build:
```shell
nx update <app-name>
```
### Testing Projects ### Testing Projects
You can run unit tests with: You can run unit tests with:
@ -269,6 +291,8 @@ Below table is a map between expo commands and Nx commands:
| `expo install` | `nx install <app-name>` | | `expo install` | `nx install <app-name>` |
| `eas build` | `nx build <app-name>` | | `eas build` | `nx build <app-name>` |
| `eas build:list` | `nx build-list <app-name>` | | `eas build:list` | `nx build-list <app-name>` |
| `eas update` | `nx update <app-name>` |
| `eas submit` | `nx submit <app-name>` |
## More Documentation ## More Documentation

View File

@ -92,15 +92,15 @@ describe('expo', () => {
expect(prebuildResult.combinedOutput).toContain('Config synced'); expect(prebuildResult.combinedOutput).toContain('Config synced');
}); });
// TODO(emily): expo-cli always fetches the latest version of react native // TODO(emily): this test failed due to version conflict with conflict with @config-plugins/detox
// re-enable it when expo-cli is fixed // https://github.com/expo/config-plugins/issues/178
xit('should install', async () => { xit('should install', async () => {
// run install command // run install command
const installResults = await runCLIAsync( const installResults = await runCLIAsync(
`install ${appName} --no-interactive --check` `install ${appName} --no-interactive`
); );
expect(installResults.combinedOutput).toContain( expect(installResults.combinedOutput).toContain(
'Dependencies are up to date' 'Successfully ran target install'
); );
}); });

View File

@ -208,7 +208,8 @@
"magic-string": "~0.26.2", "magic-string": "~0.26.2",
"markdown-factory": "^0.0.6", "markdown-factory": "^0.0.6",
"memfs": "^3.0.1", "memfs": "^3.0.1",
"metro-resolver": "^0.74.1", "metro-config": "0.76.7",
"metro-resolver": "0.76.7",
"mini-css-extract-plugin": "~2.4.7", "mini-css-extract-plugin": "~2.4.7",
"minimatch": "3.0.5", "minimatch": "3.0.5",
"next-sitemap": "^3.1.10", "next-sitemap": "^3.1.10",

View File

@ -342,12 +342,6 @@ describe('detox application generator', () => {
'cd ../../../my-dir/my-app/android && ./gradlew assembleDebug assembleAndroidTest -DtestBuildType=debug', 'cd ../../../my-dir/my-app/android && ./gradlew assembleDebug assembleAndroidTest -DtestBuildType=debug',
type: 'android.apk', type: 'android.apk',
}, },
'android.eas': {
binaryPath: '../../../my-dir/my-app/dist/MyDirMyApp.apk',
build:
'npx nx run my-dir-my-app:download --platform android --distribution simulator --output=../../../my-dir/my-app/dist/',
type: 'android.apk',
},
'android.local': { 'android.local': {
binaryPath: '../../../my-dir/my-app/dist/MyDirMyApp.apk', binaryPath: '../../../my-dir/my-app/dist/MyDirMyApp.apk',
build: build:
@ -368,12 +362,6 @@ describe('detox application generator', () => {
"cd ../../../my-dir/my-app/ios && xcodebuild -workspace MyDirMyApp.xcworkspace -scheme MyDirMyApp -configuration Debug -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 14' -derivedDataPath ./build -quiet", "cd ../../../my-dir/my-app/ios && xcodebuild -workspace MyDirMyApp.xcworkspace -scheme MyDirMyApp -configuration Debug -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 14' -derivedDataPath ./build -quiet",
type: 'ios.app', type: 'ios.app',
}, },
'ios.eas': {
binaryPath: '../../../my-dir/my-app/dist/MyDirMyApp.app',
build:
'npx nx run my-dir-my-app:download --platform ios --distribution simulator --output=../../../my-dir/my-app/dist/',
type: 'ios.app',
},
'ios.local': { 'ios.local': {
binaryPath: '../../../my-dir/my-app/dist/MyDirMyApp.app', binaryPath: '../../../my-dir/my-app/dist/MyDirMyApp.app',
build: build:

View File

@ -20,11 +20,6 @@
"binaryPath": "<%= offsetFromRoot %><%= appRoot %>/ios/build/Build/Products/Release-iphonesimulator/<%= appClassName %>.app" "binaryPath": "<%= offsetFromRoot %><%= appRoot %>/ios/build/Build/Products/Release-iphonesimulator/<%= appClassName %>.app"
}, },
<% if (framework === 'expo') { %> <% if (framework === 'expo') { %>
"ios.eas": {
"type": "ios.app",
"build": "<%= exec %> nx run <%= appFileName %>:download --platform ios --distribution simulator --output=<%= offsetFromRoot %><%= appRoot %>/dist/",
"binaryPath": "<%= offsetFromRoot %><%= appRoot %>/dist/<%= appExpoName %>.app"
},
"ios.local": { "ios.local": {
"type": "ios.app", "type": "ios.app",
"build": "<%= exec %> nx run <%= appFileName %>:build --platform ios --profile preview --wait --local --no-interactive --output=<%= offsetFromRoot %><%= appRoot %>/dist/<%= appExpoName %>.tar.gz", "build": "<%= exec %> nx run <%= appFileName %>:build --platform ios --profile preview --wait --local --no-interactive --output=<%= offsetFromRoot %><%= appRoot %>/dist/<%= appExpoName %>.tar.gz",
@ -42,11 +37,6 @@
"binaryPath": "<%= offsetFromRoot %><%= appRoot %>/android/app/build/outputs/apk/release/app-release.apk" "binaryPath": "<%= offsetFromRoot %><%= appRoot %>/android/app/build/outputs/apk/release/app-release.apk"
}, },
<% if (framework === 'expo') { %> <% if (framework === 'expo') { %>
"android.eas": {
"type": "android.apk",
"build": "<%= exec %> nx run <%= appFileName %>:download --platform android --distribution simulator --output=<%= offsetFromRoot %><%= appRoot %>/dist/",
"binaryPath": "<%= offsetFromRoot %><%= appRoot %>/dist/<%= appExpoName %>.apk"
},
"android.local": { "android.local": {
"type": "android.apk", "type": "android.apk",
"build": "<%= exec %> nx run <%= appFileName %>:build --platform android --profile preview --wait --local --no-interactive --output=<%= offsetFromRoot %><%= appRoot %>/dist/<%= appExpoName %>.apk", "build": "<%= exec %> nx run <%= appFileName %>:build --platform android --profile preview --wait --local --no-interactive --output=<%= offsetFromRoot %><%= appRoot %>/dist/<%= appExpoName %>.apk",
@ -78,10 +68,6 @@
"app": "ios.debug" "app": "ios.debug"
}, },
<% if (framework === 'expo') { %> <% if (framework === 'expo') { %>
"ios.sim.eas": {
"device": "simulator",
"app": "ios.eas"
},
"ios.sim.local": { "ios.sim.local": {
"device": "simulator", "device": "simulator",
"app": "ios.local" "app": "ios.local"
@ -96,10 +82,6 @@
"app": "android.debug" "app": "android.debug"
}, },
<% if (framework === 'expo') { %> <% if (framework === 'expo') { %>
"android.emu.eas": {
"device": "emulator",
"app": "android.eas"
},
"android.emu.local": { "android.emu.local": {
"device": "emulator", "device": "emulator",
"app": "android.local" "app": "android.local"

View File

@ -4,14 +4,10 @@ describe('getTargets', () => {
it('should return ios test target for expo projects', () => { it('should return ios test target for expo projects', () => {
expect(expoTestTarget('ios.sim', 'test')).toEqual({ expect(expoTestTarget('ios.sim', 'test')).toEqual({
options: { options: {
detoxConfiguration: 'ios.sim.eas', detoxConfiguration: 'ios.sim.local',
buildTarget: 'test:build-ios', buildTarget: 'test:build-ios',
}, },
configurations: { configurations: {
local: {
detoxConfiguration: 'ios.sim.local',
buildTarget: 'test:build-ios:local',
},
bare: { bare: {
detoxConfiguration: 'ios.sim.debug', detoxConfiguration: 'ios.sim.debug',
buildTarget: 'test:build-ios:bare', buildTarget: 'test:build-ios:bare',
@ -27,12 +23,9 @@ describe('getTargets', () => {
it('should return ios build target for expo projects', () => { it('should return ios build target for expo projects', () => {
expect(expoBuildTarget('ios.sim')).toEqual({ expect(expoBuildTarget('ios.sim')).toEqual({
options: { options: {
detoxConfiguration: 'ios.sim.eas',
},
configurations: {
local: {
detoxConfiguration: 'ios.sim.local', detoxConfiguration: 'ios.sim.local',
}, },
configurations: {
bare: { bare: {
detoxConfiguration: 'ios.sim.debug', detoxConfiguration: 'ios.sim.debug',
}, },

View File

@ -14,12 +14,9 @@ export function reactNativeBuildTarget(platform: 'ios.sim' | 'android.emu') {
export function expoBuildTarget(platform: 'ios.sim' | 'android.emu') { export function expoBuildTarget(platform: 'ios.sim' | 'android.emu') {
return { return {
options: { options: {
detoxConfiguration: `${platform}.eas`,
},
configurations: {
local: {
detoxConfiguration: `${platform}.local`, detoxConfiguration: `${platform}.local`,
}, },
configurations: {
bare: { bare: {
detoxConfiguration: `${platform}.debug`, detoxConfiguration: `${platform}.debug`,
}, },
@ -58,14 +55,10 @@ export function expoTestTarget(
return { return {
options: { options: {
detoxConfiguration: `${platform}.eas`, detoxConfiguration: `${platform}.local`,
buildTarget: `${e2eName}:build-${buildPlatform}`, buildTarget: `${e2eName}:build-${buildPlatform}`,
}, },
configurations: { configurations: {
local: {
detoxConfiguration: `${platform}.local`,
buildTarget: `${e2eName}:build-${buildPlatform}:local`,
},
bare: { bare: {
detoxConfiguration: `${platform}.debug`, detoxConfiguration: `${platform}.debug`,
buildTarget: `${e2eName}:build-${buildPlatform}:bare`, buildTarget: `${e2eName}:build-${buildPlatform}:bare`,

View File

@ -65,6 +65,18 @@
"cli": "nx", "cli": "nx",
"description": "Add app.json for detox", "description": "Add app.json for detox",
"factory": "./src/migrations/update-16-1-4/add-detox-app-json" "factory": "./src/migrations/update-16-1-4/add-detox-app-json"
},
"update-16-6-0-add-dependsOn": {
"cli": "nx",
"version": "16.6.0-beta.0",
"description": "Add dependsOn like ensure-symlink or sync-deps to targets",
"implementation": "./src/migrations/update-16-6-0/add-depends-on"
},
"update-16-6-0-update-metro-config": {
"cli": "nx",
"version": "16.6.0-beta.0",
"description": "Update metro.config.js to use the new metro config format",
"implementation": "./src/migrations/update-16-6-0/update-metro-config"
} }
}, },
"packageJsonUpdates": { "packageJsonUpdates": {
@ -855,6 +867,67 @@
"alwaysAddToPackageJson": false "alwaysAddToPackageJson": false
} }
} }
},
"16.6.0": {
"version": "16.6.0-beta.0",
"packages": {
"expo": {
"version": "^49.0.3",
"alwaysAddToPackageJson": false
},
"@expo/metro-config": {
"version": "~0.10.6",
"alwaysAddToPackageJson": false
},
"expo-splash-screen": {
"version": "~0.20.4",
"alwaysAddToPackageJson": false
},
"expo-status-bar": {
"version": "~1.6.0",
"alwaysAddToPackageJson": false
},
"@expo/cli": {
"version": "~0.10.10",
"alwaysAddToPackageJson": false
},
"eas-cli": {
"version": "~3.15.0",
"alwaysAddToPackageJson": false
},
"babel-preset-expo": {
"version": "~9.5.0",
"alwaysAddToPackageJson": false
},
"jest-expo": {
"version": "~49.0.0",
"alwaysAddToPackageJson": false
},
"metro-resolver": {
"version": "0.76.7",
"alwaysAddToPackageJson": false
},
"metro": {
"version": "0.76.7",
"alwaysAddToPackageJson": false
},
"react-native": {
"version": "0.72.3",
"alwaysAddToPackageJson": false
},
"@types/react-native": {
"version": "0.72.2",
"alwaysAddToPackageJson": false
},
"react-native-web": {
"version": "~0.19.6",
"alwaysAddToPackageJson": false
},
"react-native-svg": {
"version": "13.9.0",
"alwaysAddToPackageJson": false
}
}
} }
} }
} }

View File

@ -29,7 +29,8 @@
"chalk": "^4.1.0", "chalk": "^4.1.0",
"enhanced-resolve": "^5.8.3", "enhanced-resolve": "^5.8.3",
"fs-extra": "^11.1.0", "fs-extra": "^11.1.0",
"metro-resolver": "^0.74.1", "metro-config": "0.76.7",
"metro-resolver": "0.76.7",
"node-fetch": "^2.6.7", "node-fetch": "^2.6.7",
"tar-fs": "^2.1.1", "tar-fs": "^2.1.1",
"tsconfig-paths": "^4.1.2", "tsconfig-paths": "^4.1.2",
@ -43,7 +44,9 @@
"@nx/webpack": "file:../webpack" "@nx/webpack": "file:../webpack"
}, },
"peerDependencies": { "peerDependencies": {
"expo": "^48.0.19" "expo": ">= 49.0.0",
"@expo/cli": ">= 0.10.0",
"eas-cli": ">= 3.15.0"
}, },
"builders": "./executors.json", "builders": "./executors.json",
"ng-update": { "ng-update": {

View File

@ -1,4 +1,6 @@
import { workspaceLayout, workspaceRoot } from '@nx/devkit'; import { workspaceLayout, workspaceRoot } from '@nx/devkit';
import { mergeConfig } from 'metro-config';
import type { MetroConfig } from 'metro-config';
import { join } from 'path'; import { join } from 'path';
import { existsSync } from 'fs-extra'; import { existsSync } from 'fs-extra';
@ -7,35 +9,39 @@ import { getResolveRequest } from './metro-resolver';
interface WithNxOptions { interface WithNxOptions {
debug?: boolean; debug?: boolean;
extensions?: string[]; extensions?: string[];
/**
* @deprecated TODO(v17) in the metro.config.js, pass in to the getDefaultConfig instead: getDefaultConfig(__dirname)
*/
projectRoot?: string; projectRoot?: string;
watchFolders?: string[]; watchFolders?: string[];
} }
export function withNxMetro(config: any, opts: WithNxOptions = {}) { export async function withNxMetro(
userConfig: MetroConfig,
opts: WithNxOptions = {}
) {
const extensions = ['', 'ts', 'tsx', 'js', 'jsx', 'json']; const extensions = ['', 'ts', 'tsx', 'js', 'jsx', 'json'];
if (opts.debug) process.env.NX_REACT_NATIVE_DEBUG = 'true'; if (opts.debug) process.env.NX_REACT_NATIVE_DEBUG = 'true';
if (opts.extensions) extensions.push(...opts.extensions); if (opts.extensions) extensions.push(...opts.extensions);
config.projectRoot = opts.projectRoot || workspaceRoot; let watchFolders = [
// Add support for paths specified by tsconfig
config.resolver = {
...config.resolver,
resolveRequest: getResolveRequest(extensions),
};
let watchFolders = config.watchFolders || [];
watchFolders = watchFolders.concat([
join(workspaceRoot, 'node_modules'), join(workspaceRoot, 'node_modules'),
join(workspaceRoot, workspaceLayout().libsDir), join(workspaceRoot, workspaceLayout().libsDir),
join(workspaceRoot, 'packages'), join(workspaceRoot, 'packages'),
]); join(workspaceRoot, '.storybook'),
];
if (opts.watchFolders?.length) { if (opts.watchFolders?.length) {
watchFolders = watchFolders.concat(opts.watchFolders); watchFolders = watchFolders.concat(opts.watchFolders);
} }
watchFolders = watchFolders.filter((folder) => existsSync(folder)); watchFolders = watchFolders.filter((folder) => existsSync(folder));
config.watchFolders = watchFolders;
return config; const nxConfig: MetroConfig = {
resolver: {
resolveRequest: getResolveRequest(extensions),
},
watchFolders,
};
return mergeConfig(userConfig, nxConfig);
} }

View File

@ -2,7 +2,7 @@ import { TsconfigPathsPlugin } from 'tsconfig-paths-webpack-plugin';
import { resolve } from 'path'; import { resolve } from 'path';
/** /**
* @deprecated use bundler: 'metro' instead, will be removed in 16.0.0 * @deprecated TODO(v17) use bundler: 'metro' instead, will be removed in 16.0.0
* This function add additional rules to expo's webpack config to make expo web working * This function add additional rules to expo's webpack config to make expo web working
*/ */
export async function withNxWebpack(config) { export async function withNxWebpack(config) {

View File

@ -1,5 +1,5 @@
import { ExecutorContext, logger, names } from '@nx/devkit'; import { ExecutorContext, logger, names } from '@nx/devkit';
import { join } from 'path'; import { resolve as pathResolve } from 'path';
import { ChildProcess, fork } from 'child_process'; import { ChildProcess, fork } from 'child_process';
import { ensureNodeModulesSymlink } from '../../utils/ensure-node-modules-symlink'; import { ensureNodeModulesSymlink } from '../../utils/ensure-node-modules-symlink';
@ -42,10 +42,10 @@ export function runCliBuildList(
): Promise<string> { ): Promise<string> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
childProcess = fork( childProcess = fork(
join(workspaceRoot, './node_modules/eas-cli/bin/run'), require.resolve('eas-cli/bin/run'),
['build:list', ...createBuildListOptions(options)], ['build:list', ...createBuildListOptions(options)],
{ {
cwd: join(workspaceRoot, projectRoot), cwd: pathResolve(workspaceRoot, projectRoot),
env: process.env, env: process.env,
stdio: ['inherit', 'pipe', 'inherit', 'ipc'], // only stream stdout on child process stdio: ['inherit', 'pipe', 'inherit', 'ipc'], // only stream stdout on child process
} }

View File

@ -1,5 +1,5 @@
import { ExecutorContext, names } from '@nx/devkit'; import { ExecutorContext, names, output } from '@nx/devkit';
import { join, normalize, sep } from 'path'; import { normalize, sep, resolve as pathResolve, dirname } from 'path';
import { ChildProcess, fork } from 'child_process'; import { ChildProcess, fork } from 'child_process';
import { ensureNodeModulesSymlink } from '../../utils/ensure-node-modules-symlink'; import { ensureNodeModulesSymlink } from '../../utils/ensure-node-modules-symlink';
@ -20,21 +20,22 @@ export default async function* buildExecutor(
): AsyncGenerator<ReactNativeBuildOutput> { ): AsyncGenerator<ReactNativeBuildOutput> {
const projectRoot = const projectRoot =
context.projectsConfigurations.projects[context.projectName].root; context.projectsConfigurations.projects[context.projectName].root;
ensureNodeModulesSymlink(context.root, projectRoot);
try { try {
// remove the output app if it already existed // remove the output app if it already existed
if (options.local && options.output) { if (options.local && options.output) {
removeSync(options.output); removeSync(options.output);
if (options.output.endsWith('.tar.gz')) {
// remove unzipped app if it already existed
removeSync(options.output.replace('.tar.gz', '.app'));
}
} }
await runCliBuild(context.root, projectRoot, options); await runCliBuild(context.root, projectRoot, options);
// unzip the build if it's a tar.gz // unzip the build if it's a tar.gz
if (options.local && options.output && options.output.endsWith('.tar.gz')) { if (options.local && options.output && options.output.endsWith('.tar.gz')) {
const directoryPath = normalize(options.output).split(sep); const outputDirectory = dirname(options.output);
directoryPath.pop();
const outputDirectory = directoryPath.join(sep);
await unzipBuild(options.output, outputDirectory); await unzipBuild(options.output, outputDirectory);
} }
yield { success: true }; yield { success: true };
@ -52,10 +53,10 @@ function runCliBuild(
) { ) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
childProcess = fork( childProcess = fork(
join(workspaceRoot, './node_modules/eas-cli/bin/run'), require.resolve('eas-cli/bin/run'),
['build', ...createBuildOptions(options)], ['build', ...createBuildOptions(options)],
{ {
cwd: join(workspaceRoot, projectRoot), cwd: pathResolve(workspaceRoot, projectRoot),
env: process.env, env: process.env,
} }
); );

View File

@ -55,7 +55,8 @@
}, },
"output": { "output": {
"type": "string", "type": "string",
"description": "Output path for local build" "description": "Output path for local build",
"examples": ["../../dist/MyApp.tar.gz", "../../dist"]
}, },
"wait": { "wait": {
"type": "boolean", "type": "boolean",

View File

@ -28,6 +28,7 @@ export interface ReactNativeDownloadOutput {
const streamPipeline = promisify(pipeline); const streamPipeline = promisify(pipeline);
/** /**
* @deprecated TODO(v17) this executor is to be removed in NX 17. It is no longer used.
* This executor downloads the latest EAS build. * This executor downloads the latest EAS build.
* It calls the build list executor to list EAS builds with options passed in. * It calls the build list executor to list EAS builds with options passed in.
*/ */

View File

@ -1,8 +1,7 @@
import { ExecutorContext, names } from '@nx/devkit'; import { ExecutorContext, names } from '@nx/devkit';
import { ChildProcess, fork } from 'child_process'; import { ChildProcess, fork } from 'child_process';
import { join } from 'path'; import { resolve as pathResolve } from 'path';
import { ensureNodeModulesSymlink } from '../../utils/ensure-node-modules-symlink';
import { ExportExecutorSchema } from './schema'; import { ExportExecutorSchema } from './schema';
export interface ExpoExportOutput { export interface ExpoExportOutput {
@ -17,7 +16,6 @@ export default async function* exportExecutor(
): AsyncGenerator<ExpoExportOutput> { ): AsyncGenerator<ExpoExportOutput> {
const projectRoot = const projectRoot =
context.projectsConfigurations.projects[context.projectName].root; context.projectsConfigurations.projects[context.projectName].root;
ensureNodeModulesSymlink(context.root, projectRoot);
try { try {
await exportAsync(context.root, projectRoot, options); await exportAsync(context.root, projectRoot, options);
@ -39,13 +37,13 @@ function exportAsync(
): Promise<number> { ): Promise<number> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
childProcess = fork( childProcess = fork(
join(workspaceRoot, './node_modules/@expo/cli/build/bin/cli'), require.resolve('@expo/cli/build/bin/cli'),
[ [
`export${options.bundler === 'webpack' ? ':web' : ''}`, `export${options.bundler === 'webpack' ? ':web' : ''}`,
'.', '.',
...createExportOptions(options), ...createExportOptions(options),
], ],
{ cwd: join(workspaceRoot, projectRoot), env: process.env } { cwd: pathResolve(workspaceRoot, projectRoot), env: process.env }
); );
// Ensure the child process is killed when the parent exits // Ensure the child process is killed when the parent exits

View File

@ -1,6 +1,5 @@
import { ExecutorContext, names } from '@nx/devkit'; import { ExecutorContext, names } from '@nx/devkit';
import { ChildProcess, fork } from 'child_process'; import { ChildProcess, fork } from 'child_process';
import { join } from 'path';
import { ensureNodeModulesSymlink } from '../../utils/ensure-node-modules-symlink'; import { ensureNodeModulesSymlink } from '../../utils/ensure-node-modules-symlink';
import { ExpoInstallOptions } from './schema'; import { ExpoInstallOptions } from './schema';
@ -38,7 +37,7 @@ export function installAsync(
): Promise<number> { ): Promise<number> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
childProcess = fork( childProcess = fork(
join(workspaceRoot, './node_modules/@expo/cli/build/bin/cli'), require.resolve('@expo/cli/build/bin/cli'),
['install', ...createInstallOptions(options)], ['install', ...createInstallOptions(options)],
{ cwd: workspaceRoot, env: process.env } { cwd: workspaceRoot, env: process.env }
); );

View File

@ -27,7 +27,7 @@ export default async function* prebuildExecutor(
if (options.install) { if (options.install) {
await installAsync(context.root, {}); await installAsync(context.root, {});
if (options.platform === 'ios') { if (options.platform === 'ios') {
await podInstall(join(context.root, projectRoot, 'ios')); podInstall(join(context.root, projectRoot, 'ios'));
} }
} }
@ -48,7 +48,7 @@ export function prebuildAsync(
): Promise<number> { ): Promise<number> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
childProcess = fork( childProcess = fork(
join(workspaceRoot, './node_modules/@expo/cli/build/bin/cli'), require.resolve('@expo/cli/build/bin/cli'),
['prebuild', ...createPrebuildOptions(options), '--no-install'], ['prebuild', ...createPrebuildOptions(options), '--no-install'],
{ cwd: join(workspaceRoot, projectRoot), env: process.env } { cwd: join(workspaceRoot, projectRoot), env: process.env }
); );

View File

@ -1,5 +1,5 @@
import { ExecutorContext, names } from '@nx/devkit'; import { ExecutorContext, names } from '@nx/devkit';
import { join } from 'path'; import { join, resolve as pathResolve } from 'path';
import { ChildProcess, fork } from 'child_process'; import { ChildProcess, fork } from 'child_process';
import { platform } from 'os'; import { platform } from 'os';
import { existsSync } from 'fs-extra'; import { existsSync } from 'fs-extra';
@ -29,18 +29,6 @@ export default async function* runExecutor(
} }
const projectRoot = const projectRoot =
context.projectsConfigurations.projects[context.projectName].root; 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 (!existsSync(join(context.root, projectRoot, options.platform))) { if (!existsSync(join(context.root, projectRoot, options.platform))) {
await prebuildAsync(context.root, projectRoot, { await prebuildAsync(context.root, projectRoot, {
@ -53,7 +41,7 @@ export default async function* runExecutor(
if (options.install) { if (options.install) {
await installAsync(context.root, {}); await installAsync(context.root, {});
if (options.platform === 'ios') { if (options.platform === 'ios') {
await podInstall(join(context.root, projectRoot, 'ios')); podInstall(join(context.root, projectRoot, 'ios'));
} }
} }
@ -75,10 +63,11 @@ function runCliRun(
) { ) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
childProcess = fork( childProcess = fork(
join(workspaceRoot, './node_modules/@expo/cli/build/bin/cli'), require.resolve('@expo/cli/build/bin/cli'),
['run:' + options.platform, ...createRunOptions(options), '--no-install'], // pass in no-install to prevent node_modules install ['run:' + options.platform, ...createRunOptions(options), '--no-install'], // pass in no-install to prevent node_modules install
{ {
cwd: projectRoot, cwd: pathResolve(workspaceRoot, projectRoot),
env: process.env,
} }
); );

View File

@ -6,6 +6,9 @@
export interface ExpoRunOptions { export interface ExpoRunOptions {
// nx options // nx options
platform: 'ios' | 'android'; platform: 'ios' | 'android';
/**
* @deprecated TODO(v17) Add sync-deps to dependsOn in project.json for this target instead
*/
sync: boolean; // default is true sync: boolean; // default is true
install?: boolean; // default is true install?: boolean; // default is true
clean?: boolean; // default is false clean?: boolean; // default is false

View File

@ -39,6 +39,7 @@
"type": "boolean", "type": "boolean",
"description": "Syncs npm dependencies to package.json (for React Native autolink).", "description": "Syncs npm dependencies to package.json (for React Native autolink).",
"default": true, "default": true,
"x-deprecated": "Add sync-deps to dependsOn in project.json for this target instead",
"x-priority": "internal" "x-priority": "internal"
}, },
"port": { "port": {

View File

@ -20,6 +20,8 @@ export interface ExpoStartOptions {
tunnel?: boolean; tunnel?: boolean;
offline?: boolean; offline?: boolean;
// nx options /**
* @deprecated TODO(v17) Add sync-deps to dependsOn in project.json for this target instead
*/
sync?: boolean; // default is true sync?: boolean; // default is true
} }

View File

@ -91,6 +91,7 @@
"sync": { "sync": {
"type": "boolean", "type": "boolean",
"description": "Syncs npm dependencies to package.json (for React Native autolink).", "description": "Syncs npm dependencies to package.json (for React Native autolink).",
"x-deprecated": "Add sync-deps to dependsOn in project.json for this target instead",
"default": true "default": true
} }
}, },

View File

@ -1,14 +1,9 @@
import * as chalk from 'chalk'; import * as chalk from 'chalk';
import { ExecutorContext, logger, names } from '@nx/devkit'; import { ExecutorContext, logger, names } from '@nx/devkit';
import { ChildProcess, fork } from 'child_process'; import { ChildProcess, fork } from 'child_process';
import { join } from 'path'; import { resolve as pathResolve } from 'path';
import { ensureNodeModulesSymlink } from '../../utils/ensure-node-modules-symlink';
import { ExpoStartOptions } from './schema'; import { ExpoStartOptions } from './schema';
import {
displayNewlyAddedDepsMessage,
syncDeps,
} from '../sync-deps/sync-deps.impl';
export interface ExpoStartOutput { export interface ExpoStartOutput {
baseUrl?: string; baseUrl?: string;
@ -23,18 +18,6 @@ export default async function* startExecutor(
): AsyncGenerator<ExpoStartOutput> { ): AsyncGenerator<ExpoStartOutput> {
const projectRoot = const projectRoot =
context.projectsConfigurations.projects[context.projectName].root; 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
)
);
}
try { try {
const baseUrl = `http://localhost:${options.port}`; const baseUrl = `http://localhost:${options.port}`;
@ -60,9 +43,9 @@ function startAsync(
): Promise<number> { ): Promise<number> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
childProcess = fork( childProcess = fork(
join(workspaceRoot, './node_modules/@expo/cli/build/bin/cli'), require.resolve('@expo/cli/build/bin/cli'),
['start', ...createStartOptions(options)], ['start', ...createStartOptions(options)],
{ cwd: join(workspaceRoot, projectRoot), env: process.env } { cwd: pathResolve(workspaceRoot, projectRoot), env: process.env }
); );
// Ensure the child process is killed when the parent exits // Ensure the child process is killed when the parent exits

View File

@ -1,9 +1,7 @@
import { ExecutorContext, names } from '@nx/devkit'; import { ExecutorContext, names } from '@nx/devkit';
import { join } from 'path'; import { resolve as pathResolve } from 'path';
import { ChildProcess, fork } from 'child_process'; import { ChildProcess, fork } from 'child_process';
import { ensureNodeModulesSymlink } from '../../utils/ensure-node-modules-symlink';
import { SubmitExecutorSchema } from './schema'; import { SubmitExecutorSchema } from './schema';
export interface ReactNativeSubmitOutput { export interface ReactNativeSubmitOutput {
@ -18,7 +16,6 @@ export default async function* submitExecutor(
): AsyncGenerator<ReactNativeSubmitOutput> { ): AsyncGenerator<ReactNativeSubmitOutput> {
const projectRoot = const projectRoot =
context.projectsConfigurations.projects[context.projectName].root; context.projectsConfigurations.projects[context.projectName].root;
ensureNodeModulesSymlink(context.root, projectRoot);
try { try {
await runCliSubmit(context.root, projectRoot, options); await runCliSubmit(context.root, projectRoot, options);
@ -38,10 +35,10 @@ function runCliSubmit(
) { ) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
childProcess = fork( childProcess = fork(
join(workspaceRoot, './node_modules/eas-cli/bin/run'), require.resolve('eas-cli/bin/run'),
['submit', ...createSubmitOptions(options)], ['submit', ...createSubmitOptions(options)],
{ {
cwd: join(workspaceRoot, projectRoot), cwd: pathResolve(workspaceRoot, projectRoot),
env: process.env, env: process.env,
} }
); );

View File

@ -1,5 +1,5 @@
import { ExecutorContext, names } from '@nx/devkit'; import { ExecutorContext, names } from '@nx/devkit';
import { join } from 'path'; import { resolve as pathResolve } from 'path';
import { ChildProcess, fork } from 'child_process'; import { ChildProcess, fork } from 'child_process';
import { ensureNodeModulesSymlink } from '../../utils/ensure-node-modules-symlink'; import { ensureNodeModulesSymlink } from '../../utils/ensure-node-modules-symlink';
@ -53,9 +53,9 @@ function runCliUpdate(
) { ) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
childProcess = fork( childProcess = fork(
join(workspaceRoot, './node_modules/eas-cli/bin/run'), require.resolve('eas-cli/bin/run'),
['update', ...createUpdateOptions(options)], ['update', ...createUpdateOptions(options)],
{ cwd: join(workspaceRoot, projectRoot), env: process.env } { cwd: pathResolve(workspaceRoot, projectRoot), env: process.env }
); );
// Ensure the child process is killed when the parent exits // Ensure the child process is killed when the parent exits

View File

@ -121,12 +121,6 @@ describe('app', () => {
'cd ../../../apps/my-dir/my-app/android && ./gradlew assembleDebug assembleAndroidTest -DtestBuildType=debug', 'cd ../../../apps/my-dir/my-app/android && ./gradlew assembleDebug assembleAndroidTest -DtestBuildType=debug',
type: 'android.apk', type: 'android.apk',
}, },
'android.eas': {
binaryPath: '../../../apps/my-dir/my-app/dist/MyApp.apk',
build:
'npx nx run my-app:download --platform android --distribution simulator --output=../../../apps/my-dir/my-app/dist/',
type: 'android.apk',
},
'android.local': { 'android.local': {
binaryPath: '../../../apps/my-dir/my-app/dist/MyApp.apk', binaryPath: '../../../apps/my-dir/my-app/dist/MyApp.apk',
build: build:
@ -147,12 +141,6 @@ describe('app', () => {
"cd ../../../apps/my-dir/my-app/ios && xcodebuild -workspace MyApp.xcworkspace -scheme MyApp -configuration Debug -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 14' -derivedDataPath ./build -quiet", "cd ../../../apps/my-dir/my-app/ios && xcodebuild -workspace MyApp.xcworkspace -scheme MyApp -configuration Debug -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 14' -derivedDataPath ./build -quiet",
type: 'ios.app', type: 'ios.app',
}, },
'ios.eas': {
binaryPath: '../../../apps/my-dir/my-app/dist/MyApp.app',
build:
'npx nx run my-app:download --platform ios --distribution simulator --output=../../../apps/my-dir/my-app/dist/',
type: 'ios.app',
},
'ios.local': { 'ios.local': {
binaryPath: '../../../apps/my-dir/my-app/dist/MyApp.app', binaryPath: '../../../apps/my-dir/my-app/dist/MyApp.app',
build: build:
@ -196,12 +184,6 @@ describe('app', () => {
'cd ../../apps/my-app/android && ./gradlew assembleDebug assembleAndroidTest -DtestBuildType=debug', 'cd ../../apps/my-app/android && ./gradlew assembleDebug assembleAndroidTest -DtestBuildType=debug',
type: 'android.apk', type: 'android.apk',
}, },
'android.eas': {
binaryPath: '../../apps/my-app/dist/MyApp.apk',
build:
'npx nx run my-app:download --platform android --distribution simulator --output=../../apps/my-app/dist/',
type: 'android.apk',
},
'android.local': { 'android.local': {
binaryPath: '../../apps/my-app/dist/MyApp.apk', binaryPath: '../../apps/my-app/dist/MyApp.apk',
build: build:
@ -222,12 +204,6 @@ describe('app', () => {
"cd ../../apps/my-app/ios && xcodebuild -workspace MyApp.xcworkspace -scheme MyApp -configuration Debug -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 14' -derivedDataPath ./build -quiet", "cd ../../apps/my-app/ios && xcodebuild -workspace MyApp.xcworkspace -scheme MyApp -configuration Debug -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 14' -derivedDataPath ./build -quiet",
type: 'ios.app', type: 'ios.app',
}, },
'ios.eas': {
binaryPath: '../../apps/my-app/dist/MyApp.app',
build:
'npx nx run my-app:download --platform ios --distribution simulator --output=../../apps/my-app/dist/',
type: 'ios.app',
},
'ios.local': { 'ios.local': {
binaryPath: '../../apps/my-app/dist/MyApp.app', binaryPath: '../../apps/my-app/dist/MyApp.app',
build: build:
@ -272,12 +248,6 @@ describe('app', () => {
'cd ../../apps/my-app/android && ./gradlew assembleDebug assembleAndroidTest -DtestBuildType=debug', 'cd ../../apps/my-app/android && ./gradlew assembleDebug assembleAndroidTest -DtestBuildType=debug',
type: 'android.apk', type: 'android.apk',
}, },
'android.eas': {
binaryPath: '../../apps/my-app/dist/myappname.apk',
build:
'npx nx run my-app:download --platform android --distribution simulator --output=../../apps/my-app/dist/',
type: 'android.apk',
},
'android.local': { 'android.local': {
binaryPath: '../../apps/my-app/dist/myappname.apk', binaryPath: '../../apps/my-app/dist/myappname.apk',
build: build:
@ -298,12 +268,6 @@ describe('app', () => {
"cd ../../apps/my-app/ios && xcodebuild -workspace MyApp.xcworkspace -scheme MyApp -configuration Debug -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 14' -derivedDataPath ./build -quiet", "cd ../../apps/my-app/ios && xcodebuild -workspace MyApp.xcworkspace -scheme MyApp -configuration Debug -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 14' -derivedDataPath ./build -quiet",
type: 'ios.app', type: 'ios.app',
}, },
'ios.eas': {
binaryPath: '../../apps/my-app/dist/myappname.app',
build:
'npx nx run my-app:download --platform ios --distribution simulator --output=../../apps/my-app/dist/',
type: 'ios.app',
},
'ios.local': { 'ios.local': {
binaryPath: '../../apps/my-app/dist/myappname.app', binaryPath: '../../apps/my-app/dist/myappname.app',
build: build:

View File

@ -1,25 +1,37 @@
const { withNxMetro } = require('@nx/expo'); const { withNxMetro } = require('@nx/expo');
const { getDefaultConfig } = require('@expo/metro-config'); const { getDefaultConfig } = require('@expo/metro-config');
const { mergeConfig } = require('metro-config');
const exclusionList = require('metro-config/src/defaults/exclusionList');
const defaultConfig = getDefaultConfig(__dirname); const defaultConfig = getDefaultConfig(__dirname);
const { assetExts, sourceExts } = defaultConfig.resolver;
module.exports = (async () => { /**
defaultConfig.transformer.babelTransformerPath = require.resolve( * Metro configuration
'react-native-svg-transformer' * https://facebook.github.io/metro/docs/configuration
); *
defaultConfig.resolver.assetExts = defaultConfig.resolver.assetExts.filter( * @type {import('metro-config').MetroConfig}
(ext) => ext !== 'svg' */
); const customConfig = {
defaultConfig.resolver.sourceExts.push('svg'); transformer: {
return withNxMetro(defaultConfig, { babelTransformerPath: require.resolve('react-native-svg-transformer'),
},
resolver: {
assetExts: assetExts.filter((ext) => ext !== 'svg'),
sourceExts: [...sourceExts, 'svg'],
blockList: exclusionList([/^(?!.*node_modules).*\/dist\/.*/]),
unstable_enableSymlinks: true,
unstable_enablePackageExports: true,
},
};
module.exports = withNxMetro(mergeConfig(defaultConfig, customConfig), {
// Change this to true to see debugging info. // Change this to true to see debugging info.
// Useful if you have issues resolving modules // Useful if you have issues resolving modules
debug: false, debug: false,
// all the file extensions used for imports other than 'ts', 'tsx', 'js', 'jsx' // all the file extensions used for imports other than 'ts', 'tsx', 'js', 'jsx', 'json'
extensions: [], extensions: [],
// the project root to start the metro server // Specify folders to watch, in addition to Nx defaults (workspace libraries and node_modules)
projectRoot: __dirname, watchFolders: [],
// Specify any additional (to projectRoot) watch folders, this is used to know which files to watch });
watchFolders: []
});
})();

View File

@ -5,6 +5,7 @@
"dependencies": { "dependencies": {
"@testing-library/jest-native": "*", "@testing-library/jest-native": "*",
"@testing-library/react-native": "*", "@testing-library/react-native": "*",
"metro-config": "*",
"react-native": "*", "react-native": "*",
"expo": "*", "expo": "*",
"react-native-svg": "*", "react-native-svg": "*",

View File

@ -29,6 +29,7 @@ function getTargets(options: NormalizedSchema) {
architect.start = { architect.start = {
executor: '@nx/expo:start', executor: '@nx/expo:start',
dependsOn: ['ensure-symlink', 'sync-deps'],
options: { options: {
port: 8081, port: 8081,
}, },
@ -43,6 +44,7 @@ function getTargets(options: NormalizedSchema) {
architect['run-ios'] = { architect['run-ios'] = {
executor: '@nx/expo:run', executor: '@nx/expo:run',
dependsOn: ['ensure-symlink', 'sync-deps'],
options: { options: {
platform: 'ios', platform: 'ios',
}, },
@ -50,6 +52,7 @@ function getTargets(options: NormalizedSchema) {
architect['run-android'] = { architect['run-android'] = {
executor: '@nx/expo:run', executor: '@nx/expo:run',
dependsOn: ['ensure-symlink', 'sync-deps'],
options: { options: {
platform: 'android', platform: 'android',
}, },
@ -70,6 +73,9 @@ function getTargets(options: NormalizedSchema) {
options: {}, options: {},
}; };
/**
* @deprecated TODO(v17) this executor is no longer used, to be removed in v17
*/
architect['download'] = { architect['download'] = {
executor: '@nx/expo:download', executor: '@nx/expo:download',
options: { options: {
@ -89,6 +95,7 @@ function getTargets(options: NormalizedSchema) {
architect['prebuild'] = { architect['prebuild'] = {
executor: '@nx/expo:prebuild', executor: '@nx/expo:prebuild',
dependsOn: ['ensure-symlink', 'sync-deps'],
options: {}, options: {},
}; };
@ -104,6 +111,7 @@ function getTargets(options: NormalizedSchema) {
architect['export'] = { architect['export'] = {
executor: '@nx/expo:export', executor: '@nx/expo:export',
dependsOn: ['ensure-symlink', 'sync-deps'],
options: { options: {
platform: 'all', platform: 'all',
outputDir: `${offsetFromRoot(options.appProjectRoot)}dist/${ outputDir: `${offsetFromRoot(options.appProjectRoot)}dist/${

View File

@ -0,0 +1,21 @@
import { Tree, getProjects, updateProjectConfiguration } from '@nx/devkit';
/**
* This migration adds dependsOn to project.json.
*
*/
export default async function update(tree: Tree) {
const projects = getProjects(tree);
for (const [name, config] of projects.entries()) {
if (config.targets?.['start']?.executor === '@nx/expo:start') {
config.targets['start'].dependsOn = ['ensure-symlink', 'sync-deps'];
config.targets['run-ios'].dependsOn = ['ensure-symlink', 'sync-deps'];
config.targets['run-android'].dependsOn = ['ensure-symlink', 'sync-deps'];
config.targets['prebuild'].dependsOn = ['ensure-symlink', 'sync-deps'];
config.targets['export'].dependsOn = ['ensure-symlink', 'sync-deps'];
updateProjectConfiguration(tree, name, config);
}
}
}

View File

@ -0,0 +1,73 @@
import { Tree, getProjects } from '@nx/devkit';
import { join } from 'path';
/**
* This migration updates metro.config.js to export config as a default.
*
*/
export default async function update(tree: Tree) {
const projects = getProjects(tree);
for (const [_, config] of projects.entries()) {
if (config.targets?.['start']?.executor === '@nx/expo:start') {
if (tree.exists(join(config.root, 'metro.config.js'))) {
const oldConfig = tree
.read(join(config.root, 'metro.config.js'))
.toString();
tree.write(
join(config.root, 'metro-v71.config.js'),
oldConfigComment + oldConfig
);
tree.write(join(config.root, 'metro.config.js'), content);
}
}
}
}
const oldConfigComment = `/**
* Old custom configuration for React Native v0.71.
* From @react-native/metro-config 0.72.1, it is no longer necessary to use a config function to access the complete default config.
* Please port your custom configuration to metro.config.js.
* Please see https://docs.expo.dev/guides/customizing-metro/ to learn about configuration.
*/
`;
const content = `
const { withNxMetro } = require('@nx/expo');
const { getDefaultConfig } = require('@expo/metro-config');
const { mergeConfig } = require('metro-config');
const exclusionList = require('metro-config/src/defaults/exclusionList');
const defaultConfig = getDefaultConfig(__dirname);
const { assetExts, sourceExts } = defaultConfig.resolver;
/**
* Metro configuration
* https://facebook.github.io/metro/docs/configuration
*
* @type {import('metro-config').MetroConfig}
*/
const customConfig = {
transformer: {
babelTransformerPath: require.resolve('react-native-svg-transformer'),
},
resolver: {
assetExts: assetExts.filter((ext) => ext !== 'svg'),
sourceExts: [...sourceExts, 'svg'],
blockList: exclusionList([/^(?!.*node_modules).*\/dist\/.*/]),
unstable_enableSymlinks: true,
unstable_enablePackageExports: true,
},
};
module.exports = withNxMetro(mergeConfig(defaultConfig, customConfig), {
// Change this to true to see debugging info.
// Useful if you have issues resolving modules
debug: false,
// all the file extensions used for imports other than 'ts', 'tsx', 'js', 'jsx', 'json'
extensions: [],
// Specify folders to watch, in addition to Nx defaults (workspace libraries and node_modules)
watchFolders: [],
});
`;

View File

@ -1,28 +1,27 @@
export const nxVersion = require('../../package.json').version; export const nxVersion = require('../../package.json').version;
export const expoVersion = '48.0.19'; export const expoVersion = '49.0.3';
export const expoMetroConfigVersion = '0.7.1'; export const expoMetroConfigVersion = '~0.10.6';
export const expoSplashScreenVersion = '~0.18.2'; export const expoSplashScreenVersion = '~0.20.4';
export const expoStatusBarVersion = '~1.4.4'; export const expoStatusBarVersion = '~1.6.0';
export const expoUpdatesVersion = '~0.16.4'; export const expoCliVersion = '~0.10.10'; // @expo/cli
export const expoCliVersion = '~0.7.3'; // @expo/cli export const easCliVersion = '~3.15.1';
export const easCliVersion = '~3.13.3'; export const babelPresetExpoVersion = '~9.5.0';
export const babelPresetExpoVersion = '~9.3.2';
export const reactVersion = '18.2.0'; export const reactVersion = '18.2.0';
export const reactDomVersion = '18.2.0'; export const reactDomVersion = '18.2.0';
export const reactTestRendererVersion = '18.2.0'; export const reactTestRendererVersion = '18.2.0';
export const typesReactVersion = '18.0.28'; export const typesReactVersion = '18.0.28';
export const reactNativeVersion = '0.71.8'; export const reactNativeVersion = '0.72.3';
export const typesReactNativeVersion = '0.71.7'; export const typesReactNativeVersion = '0.72.2';
export const reactNativeWebVersion = '~0.18.12'; export const reactNativeWebVersion = '~0.19.6';
export const reactNativeSvgTransformerVersion = '1.0.0'; export const reactNativeSvgTransformerVersion = '1.0.0';
export const reactNativeSvgVersion = '13.4.0'; export const reactNativeSvgVersion = '13.9.0';
export const metroVersion = '0.74.1'; export const metroVersion = '0.76.7';
export const testingLibraryReactNativeVersion = '12.1.2'; export const testingLibraryReactNativeVersion = '12.1.2';
export const testingLibraryJestNativeVersion = '5.4.2'; export const testingLibraryJestNativeVersion = '5.4.2';
export const jestExpoVersion = '~48.0.2'; export const jestExpoVersion = '~49.0.0';

View File

@ -89,6 +89,18 @@
"version": "16.1.0-beta.0", "version": "16.1.0-beta.0",
"description": "Upgrade @storybook/react-native to 6.5", "description": "Upgrade @storybook/react-native to 6.5",
"implementation": "./src/migrations/update-16-1-0/upgrade-storybook-6-5" "implementation": "./src/migrations/update-16-1-0/upgrade-storybook-6-5"
},
"update-16-6-0-add-dependsOn": {
"cli": "nx",
"version": "16.6.0-beta.0",
"description": "Add dependsOn like ensure-symlink or sync-deps to targets",
"implementation": "./src/migrations/update-16-6-0/add-depends-on"
},
"update-16-6-0-update-metro-config": {
"cli": "nx",
"version": "16.6.0-beta.0",
"description": "Update metro.config.js to use the new metro config format",
"implementation": "./src/migrations/update-16-6-0/update-metro-config"
} }
}, },
"packageJsonUpdates": { "packageJsonUpdates": {
@ -1348,6 +1360,72 @@
"alwaysAddToPackageJson": false "alwaysAddToPackageJson": false
} }
} }
},
"16.6.0": {
"version": "16.6.0-beta.0",
"packages": {
"react-native": {
"version": "0.72.3",
"alwaysAddToPackageJson": false
},
"@types/react-native": {
"version": "0.72.2",
"alwaysAddToPackageJson": false
},
"metro": {
"version": "0.76.7",
"alwaysAddToPackageJson": false
},
"metro-resolver": {
"version": "0.76.7",
"alwaysAddToPackageJson": false
},
"metro-config": {
"version": "0.76.7",
"alwaysAddToPackageJson": false
},
"metro-react-native-babel-preset": {
"version": "0.76.7",
"alwaysAddToPackageJson": false
},
"metro-babel-register": {
"version": "0.76.7",
"alwaysAddToPackageJson": false
},
"metro-react-native-babel-transformer": {
"version": "0.76.7",
"alwaysAddToPackageJson": false
},
"@react-native/metro-config": {
"version": "^0.72.9",
"alwaysAddToPackageJson": false,
"addToPackageJson": "devDependencies"
},
"@react-native-community/cli": {
"version": "11.3.5",
"alwaysAddToPackageJson": false
},
"@react-native-community/cli-platform-android": {
"version": "11.3.5",
"alwaysAddToPackageJson": false
},
"@react-native-community/cli-platform-ios": {
"version": "11.3.5",
"alwaysAddToPackageJson": false
},
"@babel/runtime": {
"version": "7.22.6",
"alwaysAddToPackageJson": false
},
"@react-native-async-storage/async-storage": {
"version": "1.19.0",
"alwaysAddToPackageJson": false
},
"react-native-safe-area-context": {
"version": "4.7.1",
"alwaysAddToPackageJson": false
}
}
} }
} }
} }

View File

@ -30,7 +30,8 @@
"fs-extra": "^11.1.0", "fs-extra": "^11.1.0",
"glob": "7.1.4", "glob": "7.1.4",
"ignore": "^5.0.4", "ignore": "^5.0.4",
"metro-resolver": "^0.74.1", "metro-config": "0.76.7",
"metro-resolver": "0.76.7",
"minimatch": "3.0.5", "minimatch": "3.0.5",
"node-fetch": "^2.6.7", "node-fetch": "^2.6.7",
"tsconfig-paths": "^4.1.2", "tsconfig-paths": "^4.1.2",
@ -43,7 +44,7 @@
"@nx/workspace": "file:../workspace" "@nx/workspace": "file:../workspace"
}, },
"peerDependencies": { "peerDependencies": {
"react-native": ">= 0.71.0" "react-native": ">= 0.72.0 < 0.73.0"
}, },
"builders": "./executors.json", "builders": "./executors.json",
"ng-update": { "ng-update": {

View File

@ -1,4 +1,6 @@
import { workspaceLayout, workspaceRoot } from '@nx/devkit'; import { workspaceLayout, workspaceRoot } from '@nx/devkit';
import { mergeConfig } from 'metro-config';
import type { MetroConfig } from 'metro-config';
import { join } from 'path'; import { join } from 'path';
import { existsSync } from 'fs-extra'; import { existsSync } from 'fs-extra';
@ -7,36 +9,39 @@ import { getResolveRequest } from './metro-resolver';
interface WithNxOptions { interface WithNxOptions {
debug?: boolean; debug?: boolean;
extensions?: string[]; extensions?: string[];
/**
* @deprecated TODO(v17) in the metro.config.js, pass in to the getDefaultConfig instead: getDefaultConfig(__dirname)
*/
projectRoot?: string; projectRoot?: string;
watchFolders?: string[]; watchFolders?: string[];
} }
export function withNxMetro(config: any, opts: WithNxOptions = {}) { export async function withNxMetro(
userConfig: MetroConfig,
opts: WithNxOptions = {}
) {
const extensions = ['', 'ts', 'tsx', 'js', 'jsx', 'json']; const extensions = ['', 'ts', 'tsx', 'js', 'jsx', 'json'];
if (opts.debug) process.env.NX_REACT_NATIVE_DEBUG = 'true'; if (opts.debug) process.env.NX_REACT_NATIVE_DEBUG = 'true';
if (opts.extensions) extensions.push(...opts.extensions); if (opts.extensions) extensions.push(...opts.extensions);
config.projectRoot = opts.projectRoot || workspaceRoot; let watchFolders = [
// Add support for paths specified by tsconfig
config.resolver = {
...config.resolver,
resolveRequest: getResolveRequest(extensions),
};
let watchFolders = config.watchFolders || [];
watchFolders = watchFolders.concat([
join(workspaceRoot, 'node_modules'), join(workspaceRoot, 'node_modules'),
join(workspaceRoot, workspaceLayout().libsDir), join(workspaceRoot, workspaceLayout().libsDir),
join(workspaceRoot, 'packages'), join(workspaceRoot, 'packages'),
join(workspaceRoot, '.storybook'), join(workspaceRoot, '.storybook'),
]); ];
if (opts.watchFolders?.length) { if (opts.watchFolders?.length) {
watchFolders = watchFolders.concat(opts.watchFolders); watchFolders = watchFolders.concat(opts.watchFolders);
} }
watchFolders = watchFolders.filter((folder) => existsSync(folder)); watchFolders = watchFolders.filter((folder) => existsSync(folder));
config.watchFolders = watchFolders;
return config; const nxConfig: MetroConfig = {
resolver: {
resolveRequest: getResolveRequest(extensions),
},
watchFolders,
};
return mergeConfig(userConfig, nxConfig);
} }

View File

@ -1,62 +1,24 @@
import { ExecutorContext, names } from '@nx/devkit'; import { ExecutorContext } from '@nx/devkit';
import { join } from 'path'; import { join, resolve as pathResolve } from 'path';
import { ensureNodeModulesSymlink } from '../../utils/ensure-node-modules-symlink';
import { ChildProcess, fork } from 'child_process'; import { ChildProcess, fork } from 'child_process';
import { ReactNativeBuildAndroidOptions } from './schema'; import { ReactNativeBuildAndroidOptions } from './schema';
import { chmodAndroidGradlewFiles } from '../../utils/chmod-android-gradle-files'; 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'; import { getCliOptions } from '../../utils/get-cli-options';
export interface ReactNativeBuildOutput { export interface ReactNativeBuildOutput {
success: boolean; success: boolean;
} }
let childProcess: ChildProcess;
export default async function* buildAndroidExecutor( export default async function* buildAndroidExecutor(
options: ReactNativeBuildAndroidOptions, options: ReactNativeBuildAndroidOptions,
context: ExecutorContext context: ExecutorContext
): AsyncGenerator<ReactNativeBuildOutput> { ): AsyncGenerator<ReactNativeBuildOutput> {
const projectRoot = const projectRoot =
context.projectsConfigurations.projects[context.projectName].root; 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')); 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 }; yield { success: true };
} finally {
if (childProcess) {
childProcess.kill();
}
}
} }
function runCliBuild( function runCliBuild(
@ -64,30 +26,39 @@ function runCliBuild(
projectRoot: string, projectRoot: string,
options: ReactNativeBuildAndroidOptions options: ReactNativeBuildAndroidOptions
) { ) {
return new Promise((resolve, reject) => { return new Promise<ChildProcess>((res, reject) => {
/** /**
* Call the react native cli with option `--no-packager` * Call the react native cli with option `--no-packager`
* Not passing '--packager' due to cli will launch start command from the project root * Not passing '--packager' due to cli will launch start command from the project root
*/ */
childProcess = fork( const childProcess = fork(
join(workspaceRoot, './node_modules/react-native/cli.js'), require.resolve('react-native/cli.js'),
['build-android', ...createBuildAndroidOptions(options), '--no-packager'], ['build-android', ...createBuildAndroidOptions(options), '--no-packager'],
{ {
cwd: join(workspaceRoot, projectRoot), stdio: 'inherit',
cwd: pathResolve(workspaceRoot, projectRoot),
env: { ...process.env, RCT_METRO_PORT: options.port.toString() }, env: { ...process.env, RCT_METRO_PORT: options.port.toString() },
} }
); );
// Ensure the child process is killed when the parent exits /**
process.on('exit', () => childProcess.kill()); * Ensure the child process is killed when the parent exits
process.on('SIGTERM', () => childProcess.kill()); */
const processExitListener = (signal?: number | NodeJS.Signals) => () => {
childProcess.kill(signal);
process.exit();
};
process.once('exit', (signal) => childProcess.kill(signal));
process.once('SIGTERM', processExitListener);
process.once('SIGINT', processExitListener);
process.once('SIGQUIT', processExitListener);
childProcess.on('error', (err) => { childProcess.on('error', (err) => {
reject(err); reject(err);
}); });
childProcess.on('exit', (code) => { childProcess.on('exit', (code) => {
if (code === 0) { if (code === 0) {
resolve(code); res(childProcess);
} else { } else {
reject(code); reject(code);
} }

View File

@ -1,4 +1,5 @@
// options taken from https://github.com/react-native-community/cli/blob/main/packages/cli-platform-android/src/commands/buildAndroid/index.ts // options taken from https://github.com/react-native-community/cli/blob/main/packages/cli-platform-android/src/commands/buildAndroid/index.ts
// https://github.com/react-native-community/cli/blob/main/packages/cli-platform-android/README.md#build-android
import { ReactNativeStartOptions } from '../start/schema'; import { ReactNativeStartOptions } from '../start/schema';
@ -20,12 +21,13 @@ export interface ReactNativeBuildAndroidOptions
// react native options // react native options
mode: string; // default is debug mode: string; // default is debug
activeArchOnly: boolean; // default is false activeArchOnly: boolean; // default is false
port: number; // default is 8081 tasks?: string | Array<string>;
tasks?: Array<string>;
extraParams?: Array<string>; extraParams?: Array<string>;
interactive?: boolean; interactive: boolean;
// nx options // nx options
// @deprecated, no longer used
packager: boolean; // default is true packager: boolean; // default is true
// @deprecated, add to sync-deps to dependsOn
sync: boolean; sync: boolean;
} }

View File

@ -51,7 +51,8 @@
"packager": { "packager": {
"type": "boolean", "type": "boolean",
"description": "Launch packager while building", "description": "Launch packager while building",
"default": true "default": true,
"x-deprecated": "Run `nx run <project>:start` instead. Will be removed in Nx 17."
}, },
"port": { "port": {
"type": "number", "type": "number",
@ -59,10 +60,10 @@
"default": 8081 "default": 8081
}, },
"tasks": { "tasks": {
"type": "array", "oneOf": [
"items": { { "type": "array", "items": { "type": "string" } },
"type": "string" { "type": "string" }
}, ],
"description": "Run custom Gradle tasks. By default it's \"assembleDebug\". Will override passed mode and variant arguments.", "description": "Run custom Gradle tasks. By default it's \"assembleDebug\". Will override passed mode and variant arguments.",
"examples": [ "examples": [
"assembleDebug", "assembleDebug",
@ -79,8 +80,12 @@
"default": false "default": false
}, },
"extraParams": { "extraParams": {
"type": "string", "oneOf": [
"description": "Custom params passed to gradle build command" { "type": "array", "items": { "type": "string" } },
{ "type": "string" }
],
"description": "Custom params passed to gradle build command",
"examples": ["-x lint -x test"]
}, },
"interactive": { "interactive": {
"type": "boolean", "type": "boolean",
@ -89,7 +94,8 @@
"sync": { "sync": {
"type": "boolean", "type": "boolean",
"description": "Syncs npm dependencies to `package.json` (for React Native autolink).", "description": "Syncs npm dependencies to `package.json` (for React Native autolink).",
"default": true "default": true,
"x-deprecated": "Add sync-deps to dependsOn instead"
}, },
"resetCache": { "resetCache": {
"type": "boolean", "type": "boolean",

View File

@ -1,25 +1,16 @@
import { ExecutorContext } from '@nx/devkit'; import { ExecutorContext } from '@nx/devkit';
import { join } from 'path'; import { resolve as pathResolve } from 'path';
import { ChildProcess, fork } from 'child_process'; import { ChildProcess, fork } from 'child_process';
import { platform } from 'os'; 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 { ReactNativeBuildIosOptions } from './schema';
import { runCliStart } from '../start/start.impl';
import { getCliOptions } from '../../utils/get-cli-options'; import { getCliOptions } from '../../utils/get-cli-options';
export interface ReactNativeBuildIosOutput { export interface ReactNativeBuildIosOutput {
success: boolean; success: boolean;
} }
let childProcess: ChildProcess; export default async function* buildIosExecutor(
export default async function* runIosExecutor(
options: ReactNativeBuildIosOptions, options: ReactNativeBuildIosOptions,
context: ExecutorContext context: ExecutorContext
): AsyncGenerator<ReactNativeBuildIosOutput> { ): AsyncGenerator<ReactNativeBuildIosOutput> {
@ -28,77 +19,48 @@ export default async function* runIosExecutor(
} }
const projectRoot = const projectRoot =
context.projectsConfigurations.projects[context.projectName].root; 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 runCliBuildIOS(context.root, projectRoot, options);
await podInstall( return { success: true };
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( function runCliBuildIOS(
workspaceRoot: string, workspaceRoot: string,
projectRoot: string, projectRoot: string,
options: ReactNativeBuildIosOptions options: ReactNativeBuildIosOptions
) { ): Promise<ChildProcess> {
return new Promise((resolve, reject) => { return new Promise<ChildProcess>((resolve, reject) => {
/** const childProcess = fork(
* Call the react native cli with option `--no-packager` require.resolve('react-native/cli.js'),
* Not passing '--packager' due to cli will launch start command from the project root [
*/ 'build-ios',
childProcess = fork( ...createBuildIOSOptions(options),
join(workspaceRoot, './node_modules/react-native/cli.js'), ...(process.env.NX_VERBOSE_LOGGING === 'true' ? ['--verbose'] : []),
['build-ios', ...createBuildIOSOptions(options)], ],
{ {
cwd: join(workspaceRoot, projectRoot), cwd: pathResolve(workspaceRoot, projectRoot),
env: { ...process.env, RCT_METRO_PORT: options.port.toString() }, env: { ...process.env, RCT_METRO_PORT: options.port.toString() },
} }
); );
// Ensure the child process is killed when the parent exits /**
process.on('exit', () => childProcess.kill()); * Ensure the child process is killed when the parent exits
process.on('SIGTERM', () => childProcess.kill()); */
const processExitListener = (signal?: number | NodeJS.Signals) => () => {
childProcess.kill(signal);
process.exit();
};
process.once('exit', (signal) => childProcess.kill(signal));
process.once('SIGTERM', processExitListener);
process.once('SIGINT', processExitListener);
process.once('SIGQUIT', processExitListener);
childProcess.on('error', (err) => { childProcess.on('error', (err) => {
reject(err); reject(err);
}); });
childProcess.on('exit', (code) => { childProcess.on('exit', (code) => {
if (code === 0) { if (code === 0) {
resolve(code); resolve(childProcess);
} else { } else {
reject(code); reject(code);
} }

View File

@ -16,7 +16,10 @@ export interface ReactNativeBuildIosOptions extends ReactNativeStartOptions {
extraParams?: string; extraParams?: string;
// nx options // nx options
// @deprecated, no longer used
packager: boolean; // default is true packager: boolean; // default is true
// @deprecated, add to pod-install to dependsOn
install: boolean; // default is true install: boolean; // default is true
// @deprecated, add to sync-deps to dependsOn
sync: boolean; // default is true sync: boolean; // default is true
} }

View File

@ -17,10 +17,6 @@
{ {
"name": "Build iOS for a device with udid", "name": "Build iOS for a device with udid",
"keys": ["udid"] "keys": ["udid"]
},
{
"name": "Run `pod install` before building iOS app",
"keys": ["install"]
} }
], ],
"properties": { "properties": {
@ -78,17 +74,22 @@
"description": "Explicitly select which scheme and configuration to use before running a build" "description": "Explicitly select which scheme and configuration to use before running a build"
}, },
"extraParams": { "extraParams": {
"type": "string", "oneOf": [
{ "type": "array", "items": { "type": "string" } },
{ "type": "string" }
],
"description": "Custom params that will be passed to xcodebuild command." "description": "Custom params that will be passed to xcodebuild command."
}, },
"install": { "install": {
"type": "boolean", "type": "boolean",
"description": "Runs `pod install` for native modules before building iOS app.", "description": "Runs `pod install` for native modules before building iOS app.",
"x-deprecated": "Add pod-install to dependsOn in project.json for this target instead",
"default": true "default": true
}, },
"sync": { "sync": {
"type": "boolean", "type": "boolean",
"description": "Syncs npm dependencies to `package.json` (for React Native autolink).", "description": "Syncs npm dependencies to `package.json` (for React Native autolink).",
"x-deprecated": "Add sync-deps to dependsOn in project.json for this target instead",
"default": true "default": true
}, },
"resetCache": { "resetCache": {
@ -99,7 +100,8 @@
"packager": { "packager": {
"type": "boolean", "type": "boolean",
"description": "Launch packager while building", "description": "Launch packager while building",
"default": true "default": true,
"x-deprecated": "Run `nx run <project>:start` instead. Will be removed in Nx 17."
} }
}, },
"required": [], "required": [],

View File

@ -1,18 +1,14 @@
import { createDirectory } from '@nx/workspace/src/utilities/fileutils'; import { createDirectory } from '@nx/workspace/src/utilities/fileutils';
import { names, ExecutorContext } from '@nx/devkit'; import { names, ExecutorContext } from '@nx/devkit';
import { dirname, join } from 'path'; import { dirname, join, resolve as pathResolve } from 'path';
import { ChildProcess, fork } from 'child_process'; import { ChildProcess, fork } from 'child_process';
import { ensureNodeModulesSymlink } from '../../utils/ensure-node-modules-symlink';
import { ReactNativeBundleOptions } from './schema'; import { ReactNativeBundleOptions } from './schema';
export interface ReactNativeBundleOutput { export interface ReactNativeBundleOutput {
success: boolean; success: boolean;
} }
let childProcess: ChildProcess;
export default async function* bundleExecutor( export default async function* bundleExecutor(
options: ReactNativeBundleOptions, options: ReactNativeBundleOptions,
context: ExecutorContext context: ExecutorContext
@ -23,41 +19,46 @@ export default async function* bundleExecutor(
options.bundleOutput = join(context.root, options.bundleOutput); options.bundleOutput = join(context.root, options.bundleOutput);
createDirectory(dirname(options.bundleOutput)); createDirectory(dirname(options.bundleOutput));
ensureNodeModulesSymlink(context.root, projectRoot);
try {
await runCliBuild(context.root, projectRoot, options); await runCliBuild(context.root, projectRoot, options);
yield { success: true }; yield { success: true };
} finally {
if (childProcess) {
childProcess.kill();
}
}
} }
function runCliBuild( function runCliBuild(
workspaceRoot: string, workspaceRoot: string,
projectRoot: string, projectRoot: string,
options: ReactNativeBundleOptions options: ReactNativeBundleOptions
) { ): Promise<ChildProcess> {
return new Promise((resolve, reject) => { return new Promise<ChildProcess>((resolve, reject) => {
const cliOptions = createBundleOptions(options); const cliOptions = createBundleOptions(options);
childProcess = fork( const childProcess = fork(
join(workspaceRoot, './node_modules/react-native/cli.js'), require.resolve('react-native/cli.js'),
['bundle', ...cliOptions], ['bundle', ...cliOptions],
{ cwd: join(workspaceRoot, projectRoot), env: process.env } {
stdio: 'inherit',
cwd: pathResolve(workspaceRoot, projectRoot),
env: process.env,
}
); );
// Ensure the child process is killed when the parent exits /**
process.on('exit', () => childProcess.kill()); * Ensure the child process is killed when the parent exits
process.on('SIGTERM', () => childProcess.kill()); */
const processExitListener = (signal?: number | NodeJS.Signals) => () => {
childProcess.kill(signal);
process.exit();
};
process.once('exit', (signal) => childProcess.kill(signal));
process.once('SIGTERM', processExitListener);
process.once('SIGINT', processExitListener);
process.once('SIGQUIT', processExitListener);
childProcess.on('error', (err) => { childProcess.on('error', (err) => {
reject(err); reject(err);
}); });
childProcess.on('exit', (code) => { childProcess.on('exit', (code) => {
if (code === 0) { if (code === 0) {
resolve(code); resolve(childProcess);
} else { } else {
reject(code); reject(code);
} }

View File

@ -15,7 +15,7 @@ export default async function* podInstall(
const projectRoot = const projectRoot =
context.projectsConfigurations.projects[context.projectName].root; context.projectsConfigurations.projects[context.projectName].root;
const iosDirectory = join(context.root, projectRoot, 'ios'); const iosDirectory = join(context.root, projectRoot, 'ios');
await runPodInstall(iosDirectory, true, options.buildFolder)(); await runPodInstall(iosDirectory, true, options)();
yield { success: true }; yield { success: true };
} }

View File

@ -1,3 +1,5 @@
export interface ReactNativePodInstallOptions { export interface ReactNativePodInstallOptions {
buildFolder: string; buildFolder: string; // default is './build'
repoUpdate: boolean; // default is false
deployment: boolean; // default is false
} }

View File

@ -9,9 +9,19 @@
"type": "object", "type": "object",
"properties": { "properties": {
"buildFolder": { "buildFolder": {
"description": "Location for iOS build artifacts. Corresponds to Xcode's \"-derivedDataPath\". Relative to ios directory", "description": "Location for iOS build artifacts. Corresponds to Xcode's \"-derivedDataPath\". Relative to ios directory.",
"type": "string", "type": "string",
"default": "./build" "default": "./build"
},
"repoUpdate": {
"description": "Force running `pod repo update` before install.",
"type": "boolean",
"default": false
},
"deployment": {
"description": "Disallow any changes to the Podfile or the Podfile.lock during installation.",
"type": "boolean",
"default": false
} }
}, },
"required": ["buildFolder"] "required": ["buildFolder"]

View File

@ -1,14 +1,9 @@
import { ExecutorContext } from '@nx/devkit'; import { ExecutorContext } from '@nx/devkit';
import { join } from 'path'; import { join, resolve as pathResolve } from 'path';
import { ChildProcess, fork } from 'child_process'; import { ChildProcess, fork } from 'child_process';
import { ensureNodeModulesSymlink } from '../../utils/ensure-node-modules-symlink';
import {
displayNewlyAddedDepsMessage,
syncDeps,
} from '../sync-deps/sync-deps.impl';
import { ReactNativeRunAndroidOptions } from './schema'; import { ReactNativeRunAndroidOptions } from './schema';
import { runCliStart } from '../start/start.impl'; import startExecutor from '../start/start.impl';
import { chmodAndroidGradlewFiles } from '../../utils/chmod-android-gradle-files'; import { chmodAndroidGradlewFiles } from '../../utils/chmod-android-gradle-files';
import { getCliOptions } from '../../utils/get-cli-options'; import { getCliOptions } from '../../utils/get-cli-options';
@ -24,72 +19,72 @@ export default async function* runAndroidExecutor(
): AsyncGenerator<ReactNativeRunAndroidOutput> { ): AsyncGenerator<ReactNativeRunAndroidOutput> {
const projectRoot = const projectRoot =
context.projectsConfigurations.projects[context.projectName].root; context.projectsConfigurations.projects[context.projectName].root;
ensureNodeModulesSymlink(context.root, projectRoot);
chmodAndroidGradlewFiles(join(projectRoot, 'android')); chmodAndroidGradlewFiles(join(projectRoot, 'android'));
if (options.sync) { if (options.packager && options.mode !== 'Release') {
displayNewlyAddedDepsMessage( const startResults = startExecutor(
context.projectName, {
await syncDeps(
context.projectName,
projectRoot,
context.root,
context.projectGraph
)
);
}
try {
const tasks = [runCliRunAndroid(context.root, projectRoot, options)];
if (options.packager) {
tasks.push(
runCliStart(context.root, projectRoot, {
port: options.port, port: options.port,
resetCache: options.resetCache, resetCache: options.resetCache,
interactive: options.interactive, interactive: true,
}) },
context
); );
for await (const result of startResults) {
if (result.success) {
await runCliRunAndroid(context.root, projectRoot, options);
yield {
success: true,
};
} else {
return result;
}
}
} else {
await runCliRunAndroid(context.root, projectRoot, options);
} }
await Promise.all(tasks);
yield { success: true }; yield { success: true };
} finally {
if (childProcess) {
childProcess.kill();
}
}
} }
function runCliRunAndroid( function runCliRunAndroid(
workspaceRoot: string, workspaceRoot: string,
projectRoot: string, projectRoot: string,
options: ReactNativeRunAndroidOptions options: ReactNativeRunAndroidOptions
) { ): Promise<ChildProcess> {
return new Promise((resolve, reject) => { return new Promise<ChildProcess>((resolve, reject) => {
/** /**
* Call the react native cli with option `--no-packager` * Call the react native cli with option `--no-packager`
* Not passing '--packager' due to cli will launch start command from the project root * Not passing '--packager' due to cli will launch start command from the project root
*/ */
childProcess = fork( childProcess = fork(
join(workspaceRoot, './node_modules/react-native/cli.js'), require.resolve('react-native/cli.js'),
['run-android', ...createRunAndroidOptions(options), '--no-packager'], ['run-android', ...createRunAndroidOptions(options), '--no-packager'],
{ {
cwd: join(workspaceRoot, projectRoot), stdio: 'inherit',
cwd: pathResolve(workspaceRoot, projectRoot),
env: { ...process.env, RCT_METRO_PORT: options.port.toString() }, env: { ...process.env, RCT_METRO_PORT: options.port.toString() },
} }
); );
// Ensure the child process is killed when the parent exits /**
process.on('exit', () => childProcess.kill()); * Ensure the child process is killed when the parent exits
process.on('SIGTERM', () => childProcess.kill()); */
const processExitListener = (signal?: number | NodeJS.Signals) => () => {
childProcess.kill(signal);
process.exit();
};
process.once('exit', (signal) => childProcess.kill(signal));
process.once('SIGTERM', processExitListener);
process.once('SIGINT', processExitListener);
process.once('SIGQUIT', processExitListener);
childProcess.on('error', (err) => { childProcess.on('error', (err) => {
reject(err); reject(err);
}); });
childProcess.on('exit', (code) => { childProcess.on('exit', (code) => {
if (code === 0) { if (code === 0) {
resolve(code); resolve(childProcess);
} else { } else {
reject(code); reject(code);
} }

View File

@ -5,11 +5,11 @@ import { ReactNativeStartOptions } from '../start/schema';
export interface ReactNativeRunAndroidOptions export interface ReactNativeRunAndroidOptions
extends ReactNativeBuildAndroidOptions { extends ReactNativeBuildAndroidOptions {
/** /**
* @deprecated use mode instead * @deprecated TODO(v17) use mode instead
*/ */
variant: string; variant: string;
/** /**
* @deprecated no longer supported in react native cli * @deprecated TODO(v17) no longer supported in react native cli
* https://github.com/react-native-community/cli/commit/7c003f2b1d9d80ec5c167614ba533a004272c685 * https://github.com/react-native-community/cli/commit/7c003f2b1d9d80ec5c167614ba533a004272c685
*/ */
jetifier: boolean; jetifier: boolean;

View File

@ -77,10 +77,10 @@
"default": 8081 "default": 8081
}, },
"tasks": { "tasks": {
"type": "array", "oneOf": [
"items": { { "type": "array", "items": { "type": "string" } },
"type": "string" { "type": "string" }
}, ],
"description": "Run custom Gradle tasks. By default it's \"assembleDebug\". Will override passed mode and variant arguments.", "description": "Run custom Gradle tasks. By default it's \"assembleDebug\". Will override passed mode and variant arguments.",
"examples": [ "examples": [
"assembleDebug", "assembleDebug",
@ -97,7 +97,10 @@
"default": false "default": false
}, },
"extraParams": { "extraParams": {
"type": "string", "oneOf": [
{ "type": "array", "items": { "type": "string" } },
{ "type": "string" }
],
"description": "Custom params passed to gradle build command" "description": "Custom params passed to gradle build command"
}, },
"interactive": { "interactive": {
@ -107,6 +110,7 @@
"sync": { "sync": {
"type": "boolean", "type": "boolean",
"description": "Syncs npm dependencies to `package.json` (for React Native autolink).", "description": "Syncs npm dependencies to `package.json` (for React Native autolink).",
"x-deprecated": "Add sync-deps to dependsOn for this target in project.json instead",
"default": true "default": true
}, },
"resetCache": { "resetCache": {

View File

@ -1,24 +1,16 @@
import { ExecutorContext } from '@nx/devkit'; import { ExecutorContext } from '@nx/devkit';
import { join } from 'path'; import { resolve as pathResolve } from 'path';
import { ChildProcess, fork } from 'child_process'; import { ChildProcess, fork } from 'child_process';
import { platform } from 'os'; import { platform } from 'os';
import { ensureNodeModulesSymlink } from '../../utils/ensure-node-modules-symlink'; import startExecutor from '../start/start.impl';
import {
displayNewlyAddedDepsMessage,
syncDeps,
} from '../sync-deps/sync-deps.impl';
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 { getCliOptions } from '../../utils/get-cli-options';
import { ReactNativeRunIosOptions } from './schema';
export interface ReactNativeRunIosOutput { export interface ReactNativeRunIosOutput {
success: boolean; success: boolean;
} }
let childProcess: ChildProcess;
export default async function* runIosExecutor( export default async function* runIosExecutor(
options: ReactNativeRunIosOptions, options: ReactNativeRunIosOptions,
context: ExecutorContext context: ExecutorContext
@ -28,77 +20,71 @@ export default async function* runIosExecutor(
} }
const projectRoot = const projectRoot =
context.projectsConfigurations.projects[context.projectName].root; 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 = [runCliRunIOS(context.root, projectRoot, options)];
if (options.packager && options.mode !== 'Release') { if (options.packager && options.mode !== 'Release') {
tasks.push( const startResults = startExecutor(
runCliStart(context.root, projectRoot, { {
port: options.port, port: options.port,
resetCache: options.resetCache, resetCache: options.resetCache,
interactive: options.interactive, interactive: true,
}) },
context
); );
for await (const result of startResults) {
if (result.success) {
await runCliRunIOS(context.root, projectRoot, options);
yield {
success: true,
};
} else {
return result;
}
}
} else {
await runCliRunIOS(context.root, projectRoot, options);
} }
await Promise.all(tasks);
yield { success: true }; yield { success: true };
} finally {
if (childProcess) {
childProcess.kill();
}
}
} }
function runCliRunIOS( function runCliRunIOS(
workspaceRoot: string, workspaceRoot: string,
projectRoot: string, projectRoot: string,
options: ReactNativeRunIosOptions options: ReactNativeRunIosOptions
) { ): Promise<ChildProcess> {
return new Promise((resolve, reject) => { return new Promise<ChildProcess>((resolve, reject) => {
/** /**
* Call the react native cli with option `--no-packager` * Call the react native cli with option `--no-packager`
* Not passing '--packager' due to cli will launch start command from the project root * Not passing '--packager' due to cli will launch start command from the project root
*/ */
childProcess = fork( const childProcess = fork(
join(workspaceRoot, './node_modules/react-native/cli.js'), require.resolve('react-native/cli.js'),
['run-ios', ...createRunIOSOptions(options), '--no-packager'], ['run-ios', ...createRunIOSOptions(options), '--no-packager'],
{ {
cwd: join(workspaceRoot, projectRoot), stdio: 'inherit',
cwd: pathResolve(workspaceRoot, projectRoot),
env: { ...process.env, RCT_METRO_PORT: options.port.toString() }, env: { ...process.env, RCT_METRO_PORT: options.port.toString() },
} }
); );
// Ensure the child process is killed when the parent exits /**
process.on('exit', () => childProcess.kill()); * Ensure the child process is killed when the parent exits
process.on('SIGTERM', () => childProcess.kill()); */
const processExitListener = (signal?: number | NodeJS.Signals) => () => {
childProcess.kill(signal);
process.exit();
};
process.once('exit', (signal) => childProcess.kill(signal));
process.once('SIGTERM', processExitListener);
process.once('SIGINT', processExitListener);
process.once('SIGQUIT', processExitListener);
childProcess.on('error', (err) => { childProcess.on('error', (err) => {
reject(err); reject(err);
}); });
childProcess.on('exit', (code) => { childProcess.on('exit', (code) => {
if (code === 0) { if (code === 0) {
resolve(code); resolve(childProcess);
} else { } else {
reject(code); reject(code);
} }

View File

@ -4,7 +4,7 @@ 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 // 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 { export interface ReactNativeRunIosOptions extends ReactNativeBuildIosOptions {
/** /**
* @deprecated use mode instead, will be removed in nx 17. * @deprecated TODO(v17) use mode instead, will be removed in nx 17.
*/ */
xcodeConfiguration?: string; xcodeConfiguration?: string;

View File

@ -19,10 +19,6 @@
{ {
"name": "Run iOS on a device with udid", "name": "Run iOS on a device with udid",
"keys": ["udid"] "keys": ["udid"]
},
{
"name": "Run `pod install` before building iOS app",
"keys": ["install"]
} }
], ],
"properties": { "properties": {
@ -87,17 +83,22 @@
"description": "Explicitly select which scheme and configuration to use before running a build" "description": "Explicitly select which scheme and configuration to use before running a build"
}, },
"extraParams": { "extraParams": {
"type": "string", "oneOf": [
{ "type": "array", "items": { "type": "string" } },
{ "type": "string" }
],
"description": "Custom params that will be passed to xcodebuild command." "description": "Custom params that will be passed to xcodebuild command."
}, },
"install": { "install": {
"type": "boolean", "type": "boolean",
"description": "Runs `pod install` for native modules before building iOS app.", "description": "Runs `pod install` for native modules before building iOS app.",
"default": true "default": true,
"x-deprecated": "Add `pod-install` to dependsOn for this target in project.json instead."
}, },
"sync": { "sync": {
"type": "boolean", "type": "boolean",
"description": "Syncs npm dependencies to `package.json` (for React Native autolink).", "description": "Syncs npm dependencies to `package.json` (for React Native autolink).",
"x-deprecated": "Add `sync-deps` to dependsOn for this target in project.json instead.",
"default": true "default": true
}, },
"resetCache": { "resetCache": {

View File

@ -1,18 +1,15 @@
import * as chalk from 'chalk';
import { ExecutorContext, logger } from '@nx/devkit'; import { ExecutorContext, logger } from '@nx/devkit';
import { ChildProcess, fork } from 'child_process'; import { ChildProcess, fork } from 'child_process';
import { join } from 'path'; import { resolve as pathResolve } from 'path';
import { ensureNodeModulesSymlink } from '../../utils/ensure-node-modules-symlink'; import { ensureNodeModulesSymlink } from '../../utils/ensure-node-modules-symlink';
import { isPackagerRunning } from './lib/is-packager-running'; import { isPackagerRunning } from './lib/is-packager-running';
import { ReactNativeStartOptions } from './schema'; import { ReactNativeStartOptions } from './schema';
export interface ReactNativeStartOutput { export interface ReactNativeStartOutput {
baseUrl?: string; port?: number;
success: boolean; success: boolean;
} }
let childProcess: ChildProcess;
export default async function* startExecutor( export default async function* startExecutor(
options: ReactNativeStartOptions, options: ReactNativeStartOptions,
context: ExecutorContext context: ExecutorContext
@ -21,27 +18,27 @@ export default async function* startExecutor(
context.projectsConfigurations.projects[context.projectName].root; context.projectsConfigurations.projects[context.projectName].root;
ensureNodeModulesSymlink(context.root, projectRoot); ensureNodeModulesSymlink(context.root, projectRoot);
try { const startProcess = await runCliStart(context.root, projectRoot, options);
const baseUrl = `http://localhost:${options.port}`;
const appName = context.projectName;
logger.info(chalk.cyan(`Packager is ready at ${baseUrl}`));
logger.info(
`Use ${chalk.bold(`nx run-android ${appName}`)} or ${chalk.bold(
`nx run-ios ${appName}`
)} to run the native app.`
);
await runCliStart(context.root, projectRoot, options);
yield { yield {
baseUrl, port: options.port,
success: true, success: true,
}; };
} finally {
if (childProcess) { if (!startProcess) {
childProcess.kill(); return;
}
} }
await new Promise<void>((resolve) => {
const processExitListener = (signal?: number | NodeJS.Signals) => () => {
startProcess.kill(signal);
resolve();
process.exit();
};
process.once('exit', (signal) => startProcess.kill(signal));
process.once('SIGTERM', processExitListener);
process.once('SIGINT', processExitListener);
process.once('SIGQUIT', processExitListener);
});
} }
/* /*
@ -52,10 +49,10 @@ export async function runCliStart(
workspaceRoot: string, workspaceRoot: string,
projectRoot: string, projectRoot: string,
options: ReactNativeStartOptions options: ReactNativeStartOptions
): Promise<void> { ): Promise<ChildProcess> {
const result = await isPackagerRunning(options.port); const result = await isPackagerRunning(options.port);
if (result === 'running') { if (result === 'running') {
logger.info('JS server already running.'); logger.info(`JS server already running on port ${options.port}.`);
} else if (result === 'unrecognized') { } else if (result === 'unrecognized') {
logger.warn('JS server not recognized.'); logger.warn('JS server not recognized.');
} else { } else {
@ -63,7 +60,7 @@ export async function runCliStart(
logger.info('Starting JS server...'); logger.info('Starting JS server...');
try { try {
await startAsync(workspaceRoot, projectRoot, options); return await startAsync(workspaceRoot, projectRoot, options);
} catch (error) { } catch (error) {
logger.error( logger.error(
`Failed to start the packager server. Error details: ${error.message}` `Failed to start the packager server. Error details: ${error.message}`
@ -77,24 +74,34 @@ function startAsync(
workspaceRoot: string, workspaceRoot: string,
projectRoot: string, projectRoot: string,
options: ReactNativeStartOptions options: ReactNativeStartOptions
): Promise<number> { ): Promise<ChildProcess> {
return new Promise((resolve, reject) => { return new Promise<ChildProcess>((resolve, reject) => {
childProcess = fork( const childProcess = fork(
join(workspaceRoot, './node_modules/react-native/cli.js'), require.resolve('react-native/cli.js'),
['start', ...createStartOptions(options)], ['start', ...createStartOptions(options)],
{ cwd: join(workspaceRoot, projectRoot), env: process.env } {
cwd: pathResolve(workspaceRoot, projectRoot),
env: process.env,
stdio: ['inherit', 'pipe', 'pipe', 'ipc'],
}
); );
// Ensure the child process is killed when the parent exits childProcess.stdout.on('data', (data) => {
process.on('exit', () => childProcess.kill()); process.stdout.write(data);
process.on('SIGTERM', () => childProcess.kill()); if (data.toString().includes('reload the app')) {
resolve(childProcess);
}
});
childProcess.stderr.on('data', (data) => {
process.stderr.write(data);
});
childProcess.on('error', (err) => { childProcess.on('error', (err) => {
reject(err); reject(err);
}); });
childProcess.on('exit', (code) => { childProcess.on('exit', (code) => {
if (code === 0) { if (code === 0) {
resolve(code); resolve(childProcess);
} else { } else {
reject(code); reject(code);
} }

View File

@ -3,7 +3,7 @@ export interface ReactNativeStorybookOptions {
outputFile: string; outputFile: string;
pattern: string; pattern: string;
/** /**
* @deprecated going to be removed in 17 * @deprecated TODO(v17) no longer used going to be removed in 17
*/ */
silent: boolean; silent: boolean;
} }

View File

@ -7,7 +7,6 @@ import {
Tree, Tree,
} from '@nx/devkit'; } from '@nx/devkit';
import { runPodInstall } from '../../utils/pod-install-task';
import { runSymlink } from '../../utils/symlink-task'; import { runSymlink } from '../../utils/symlink-task';
import { addLinting } from '../../utils/add-linting'; import { addLinting } from '../../utils/add-linting';
import { addJest } from '../../utils/add-jest'; import { addJest } from '../../utils/add-jest';
@ -48,10 +47,6 @@ export async function reactNativeApplicationGenerator(
); );
const detoxTask = await addDetox(host, options); const detoxTask = await addDetox(host, options);
const symlinkTask = runSymlink(host.root, options.appProjectRoot); const symlinkTask = runSymlink(host.root, options.appProjectRoot);
const podInstallTask = runPodInstall(
joinPathFragments(host.root, options.iosProjectRoot),
options.install
);
const chmodTaskGradlew = chmodAndroidGradlewFilesTask( const chmodTaskGradlew = chmodAndroidGradlewFilesTask(
joinPathFragments(host.root, options.androidProjectRoot) joinPathFragments(host.root, options.androidProjectRoot)
); );
@ -66,7 +61,6 @@ export async function reactNativeApplicationGenerator(
jestTask, jestTask,
detoxTask, detoxTask,
symlinkTask, symlinkTask,
podInstallTask,
chmodTaskGradlew chmodTaskGradlew
); );
} }

View File

@ -1,6 +1,6 @@
source 'https://rubygems.org' source 'https://rubygems.org'
# You may use http://rbenv.org/ or https://rvm.io/ to install and use this version # You may use http://rbenv.org/ or https://rvm.io/ to install and use this version
ruby File.read(File.join(__dir__, '.ruby-version')).strip ruby ">= 2.6.10"
gem 'cocoapods', '~> 1.11', '>= 1.11.3' gem 'cocoapods', '~> 1.12'

View File

@ -1,8 +1,6 @@
apply plugin: "com.android.application" apply plugin: "com.android.application"
apply plugin: "com.facebook.react" apply plugin: "com.facebook.react"
import com.android.build.OutputFile
/** /**
* This is the configuration block to customize your React Native Android app. * This is the configuration block to customize your React Native Android app.
* By default you don't need to apply any configuration, just uncomment the lines you need. * By default you don't need to apply any configuration, just uncomment the lines you need.
@ -13,8 +11,8 @@ react {
// root = file("../") // root = file("../")
// The folder where the react-native NPM package is. Default is ../node_modules/react-native // The folder where the react-native NPM package is. Default is ../node_modules/react-native
// reactNativeDir = file("../node_modules/react-native") // reactNativeDir = file("../node_modules/react-native")
// The folder where the react-native Codegen package is. Default is ../node_modules/react-native-codegen // The folder where the react-native Codegen package is. Default is ../node_modules/@react-native/codegen
// codegenDir = file("../node_modules/react-native-codegen") // codegenDir = file("../node_modules/@react-native/codegen")
// The cli.js file which is the React Native CLI entrypoint. Default is ../node_modules/react-native/cli.js // The cli.js file which is the React Native CLI entrypoint. Default is ../node_modules/react-native/cli.js
// cliFile = file("../node_modules/react-native/cli.js") // cliFile = file("../node_modules/react-native/cli.js")
@ -53,14 +51,6 @@ react {
entryFile = file("../../<%= entryFile %>") entryFile = file("../../<%= entryFile %>")
} }
/**
* Set this to true to create four separate APKs instead of one,
* one for each native architecture. This is useful if you don't
* use App Bundles (https://developer.android.com/guide/app-bundle/)
* and want to have separate APKs to upload to the Play Store.
*/
def enableSeparateBuildPerCPUArchitecture = false
/** /**
* Set this to true to Run Proguard on Release builds to minify the Java bytecode. * Set this to true to Run Proguard on Release builds to minify the Java bytecode.
*/ */
@ -79,16 +69,6 @@ def enableProguardInReleaseBuilds = false
*/ */
def jscFlavor = 'org.webkit:android-jsc:+' def jscFlavor = 'org.webkit:android-jsc:+'
/**
* Private function to get the list of Native Architectures you want to build.
* This reads the value from reactNativeArchitectures in your gradle.properties
* file and works together with the --active-arch-only flag of react-native run-android.
*/
def reactNativeArchitectures() {
def value = project.getProperties().get("reactNativeArchitectures")
return value ? value.split(",") : ["armeabi-v7a", "x86", "x86_64", "arm64-v8a"]
}
android { android {
ndkVersion rootProject.ext.ndkVersion ndkVersion rootProject.ext.ndkVersion
@ -106,15 +86,6 @@ android {
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
<% } %> <% } %>
} }
splits {
abi {
reset()
enable enableSeparateBuildPerCPUArchitecture
universalApk false // If true, also generate a universal APK
include (*reactNativeArchitectures())
}
}
signingConfigs { signingConfigs {
debug { debug {
storeFile file('debug.keystore') storeFile file('debug.keystore')
@ -138,22 +109,6 @@ android {
<% } %> <% } %>
} }
} }
// applicationVariants are e.g. debug, release
applicationVariants.all { variant ->
variant.outputs.each { output ->
// For each separate APK per architecture, set a unique version code as described here:
// https://developer.android.com/studio/build/configure-apk-splits.html
// Example: versionCode 1 will generate 1001 for armeabi-v7a, 1002 for x86, etc.
def versionCodes = ["armeabi-v7a": 1, "x86": 2, "arm64-v8a": 3, "x86_64": 4]
def abi = output.getFilter(OutputFile.ABI)
if (abi != null) { // null for the universal-debug, universal-release variants
output.versionCodeOverride =
defaultConfig.versionCode * 1000 + versionCodes.get(abi)
}
}
}
} }
dependencies { dependencies {
@ -164,8 +119,6 @@ dependencies {
// The version of react-native is set by the React Native Gradle Plugin // The version of react-native is set by the React Native Gradle Plugin
implementation("com.facebook.react:react-android") implementation("com.facebook.react:react-android")
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.0.0")
debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}") debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}")
debugImplementation("com.facebook.flipper:flipper-network-plugin:${FLIPPER_VERSION}") { debugImplementation("com.facebook.flipper:flipper-network-plugin:${FLIPPER_VERSION}") {
exclude group:'com.squareup.okhttp3', module:'okhttp' exclude group:'com.squareup.okhttp3', module:'okhttp'

View File

@ -27,9 +27,6 @@ public class MainActivity extends ReactActivity {
this, this,
getMainComponentName(), getMainComponentName(),
// If you opted-in for the New Architecture, we enable the Fabric Renderer. // If you opted-in for the New Architecture, we enable the Fabric Renderer.
DefaultNewArchitectureEntryPoint.getFabricEnabled(), // fabricEnabled DefaultNewArchitectureEntryPoint.getFabricEnabled());
// If you opted-in for the New Architecture, we enable Concurrent React (i.e. React 18).
DefaultNewArchitectureEntryPoint.getConcurrentReactEnabled() // concurrentRootEnabled
);
} }
} }

View File

@ -16,7 +16,7 @@ buildscript {
mavenCentral() mavenCentral()
} }
dependencies { dependencies {
classpath("com.android.tools.build:gradle:7.3.1") classpath("com.android.tools.build:gradle")
classpath("com.facebook.react:react-native-gradle-plugin") classpath("com.facebook.react:react-native-gradle-plugin")
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion") classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion")
} }

View File

@ -25,7 +25,7 @@ android.useAndroidX=true
android.enableJetifier=true android.enableJetifier=true
# Version of flipper SDK to use with React Native # Version of flipper SDK to use with React Native
FLIPPER_VERSION=0.125.0 FLIPPER_VERSION=0.182.0
# Use this property to specify which architecture you want to build. # Use this property to specify which architecture you want to build.
# You can also override it from the CLI using # You can also override it from the CLI using

View File

@ -1,5 +1,6 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.1-all.zip
networkTimeout=10000
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists

View File

@ -14,7 +14,7 @@
@rem limitations under the License. @rem limitations under the License.
@rem @rem
@if "%DEBUG%" == "" @echo off @if "%DEBUG%"=="" @echo off
@rem ########################################################################## @rem ##########################################################################
@rem @rem
@rem Gradle startup script for Windows @rem Gradle startup script for Windows
@ -25,7 +25,8 @@
if "%OS%"=="Windows_NT" setlocal if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0 set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=. if "%DIRNAME%"=="" set DIRNAME=.
@rem This is normally unused
set APP_BASE_NAME=%~n0 set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME% set APP_HOME=%DIRNAME%
@ -40,7 +41,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1 %JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute if %ERRORLEVEL% equ 0 goto execute
echo. echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
@ -75,13 +76,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
:end :end
@rem End local scope for the variables with windows NT shell @rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd if %ERRORLEVEL% equ 0 goto mainEnd
:fail :fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code! rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 set EXIT_CODE=%ERRORLEVEL%
exit /b 1 if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd :mainEnd
if "%OS%"=="Windows_NT" endlocal if "%OS%"=="Windows_NT" endlocal

View File

@ -55,7 +55,7 @@
# Darwin, MinGW, and NonStop. # Darwin, MinGW, and NonStop.
# #
# (3) This script is generated from the Groovy template # (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project. # within the Gradle project.
# #
# You can find Gradle at https://github.com/gradle/gradle/. # You can find Gradle at https://github.com/gradle/gradle/.
@ -80,10 +80,10 @@ do
esac esac
done done
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit # This is normally unused
# shellcheck disable=SC2034
APP_NAME="Gradle"
APP_BASE_NAME=${0##*/} APP_BASE_NAME=${0##*/}
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
@ -143,12 +143,16 @@ fi
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #( case $MAX_FD in #(
max*) max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC3045
MAX_FD=$( ulimit -H -n ) || MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit" warn "Could not query maximum file descriptor limit"
esac esac
case $MAX_FD in #( case $MAX_FD in #(
'' | soft) :;; #( '' | soft) :;; #(
*) *)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC3045
ulimit -n "$MAX_FD" || ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD" warn "Could not set maximum file descriptor limit to $MAX_FD"
esac esac
@ -205,6 +209,12 @@ set -- \
org.gradle.wrapper.GradleWrapperMain \ org.gradle.wrapper.GradleWrapperMain \
"$@" "$@"
# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
fi
# Use "xargs" to parse quoted args. # Use "xargs" to parse quoted args.
# #
# With -n1 it outputs one arg per line, with the quotes and backslashes removed. # With -n1 it outputs one arg per line, with the quotes and backslashes removed.

View File

@ -1,5 +1,4 @@
rootProject.name = '<%= className %>' rootProject.name = '<%= className %>'
apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings) apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings)
include ':app' include ':app'
includeBuild('../node_modules/react-native-gradle-plugin') includeBuild('../node_modules/@react-native/gradle-plugin')

View File

@ -1,5 +1,9 @@
require_relative '<%= offsetFromRoot %>../node_modules/react-native/scripts/react_native_pods' # Resolve react_native_pods.rb with node to allow for hoisting
require_relative '<%= offsetFromRoot %>../node_modules/@react-native-community/cli-platform-ios/native_modules' require Pod::Executable.execute_command('node', ['-p',
'require.resolve(
"react-native/scripts/react_native_pods.rb",
{paths: [process.argv[1]]},
)', __dir__]).strip
platform :ios, min_ios_version_supported platform :ios, min_ios_version_supported
prepare_react_native_project! prepare_react_native_project!
@ -30,8 +34,6 @@ target '<%= className %>' do
use_react_native!( use_react_native!(
:path => config[:reactNativePath], :path => config[:reactNativePath],
# Hermes is now enabled by default. Disable by setting this flag to false. # Hermes is now enabled by default. Disable by setting this flag to false.
# Upcoming versions of React Native may rely on get_default_flags(), but
# we make it explicit here to aid in the React Native upgrade process.
:hermes_enabled => flags[:hermes_enabled], :hermes_enabled => flags[:hermes_enabled],
:fabric_enabled => flags[:fabric_enabled], :fabric_enabled => flags[:fabric_enabled],
# Enables Flipper. # Enables Flipper.
@ -49,10 +51,10 @@ target '<%= className %>' do
end end
post_install do |installer| post_install do |installer|
# https://github.com/facebook/react-native/blob/main/packages/react-native/scripts/react_native_pods.rb#L197-L202
react_native_post_install( react_native_post_install(
installer, installer,
# Set `mac_catalyst_enabled` to `true` in order to apply patches config[:reactNativePath],
# necessary for Mac Catalyst builds
:mac_catalyst_enabled => false :mac_catalyst_enabled => false
) )
__apply_Xcode_12_5_M1_post_install_workaround(installer) __apply_Xcode_12_5_M1_post_install_workaround(installer)

View File

@ -564,7 +564,7 @@
COPY_PHASE_STRIP = NO; COPY_PHASE_STRIP = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES; ENABLE_TESTABILITY = YES;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = ""; "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = i386;
GCC_C_LANGUAGE_STANDARD = gnu99; GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO; GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES; GCC_NO_COMMON_BLOCKS = YES;
@ -592,12 +592,14 @@
); );
MTL_ENABLE_DEBUG_INFO = YES; MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES; ONLY_ACTIVE_ARCH = YES;
OTHER_CFLAGS = "$(inherited)";
OTHER_CPLUSPLUSFLAGS = ( OTHER_CPLUSPLUSFLAGS = (
"$(OTHER_CFLAGS)", "$(OTHER_CFLAGS)",
"-DFOLLY_NO_CONFIG", "-DFOLLY_NO_CONFIG",
"-DFOLLY_MOBILE=1", "-DFOLLY_MOBILE=1",
"-DFOLLY_USE_LIBCPP=1", "-DFOLLY_USE_LIBCPP=1",
); );
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
SDKROOT = iphoneos; SDKROOT = iphoneos;
}; };
name = Debug; name = Debug;
@ -635,7 +637,7 @@
COPY_PHASE_STRIP = YES; COPY_PHASE_STRIP = YES;
ENABLE_NS_ASSERTIONS = NO; ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_STRICT_OBJC_MSGSEND = YES;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = ""; "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = i386;
GCC_C_LANGUAGE_STANDARD = gnu99; GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES; GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
@ -655,12 +657,14 @@
"\"$(inherited)\"", "\"$(inherited)\"",
); );
MTL_ENABLE_DEBUG_INFO = NO; MTL_ENABLE_DEBUG_INFO = NO;
OTHER_CFLAGS = "$(inherited)";
OTHER_CPLUSPLUSFLAGS = ( OTHER_CPLUSPLUSFLAGS = (
"$(OTHER_CFLAGS)", "$(OTHER_CFLAGS)",
"-DFOLLY_NO_CONFIG", "-DFOLLY_NO_CONFIG",
"-DFOLLY_MOBILE=1", "-DFOLLY_MOBILE=1",
"-DFOLLY_USE_LIBCPP=1", "-DFOLLY_USE_LIBCPP=1",
); );
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
SDKROOT = iphoneos; SDKROOT = iphoneos;
VALIDATE_PRODUCT = YES; VALIDATE_PRODUCT = YES;
}; };

View File

@ -23,14 +23,4 @@
#endif #endif
} }
/// This method controls whether the `concurrentRoot`feature of React18 is turned on or off.
///
/// @see: https://reactjs.org/blog/2022/03/29/react-v18.html
/// @note: This requires to be rendering on Fabric (i.e. on the New Architecture).
/// @return: `true` if the `concurrentRoot` feature is enabled. Otherwise, it returns `false`.
- (BOOL)concurrentRootEnabled
{
return true;
}
@end @end

View File

@ -1,38 +1,35 @@
const { withNxMetro } = require('@nx/react-native'); const { withNxMetro } = require('@nx/react-native');
const { getDefaultConfig } = require('metro-config'); const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config');
const exclusionList = require('metro-config/src/defaults/exclusionList'); const exclusionList = require('metro-config/src/defaults/exclusionList');
module.exports = (async () => { const defaultConfig = getDefaultConfig(__dirname);
const { const { assetExts, sourceExts } = defaultConfig.resolver;
resolver: { sourceExts, assetExts },
} = await getDefaultConfig(); /**
return withNxMetro( * Metro configuration
{ * https://facebook.github.io/metro/docs/configuration
*
* @type {import('metro-config').MetroConfig}
*/
const customConfig = {
transformer: { transformer: {
getTransformOptions: async () => ({
transform: {
experimentalImportSupport: false,
inlineRequires: true,
},
}),
babelTransformerPath: require.resolve('react-native-svg-transformer'), babelTransformerPath: require.resolve('react-native-svg-transformer'),
}, },
resolver: { resolver: {
assetExts: assetExts.filter((ext) => ext !== 'svg'), assetExts: assetExts.filter((ext) => ext !== 'svg'),
sourceExts: [...sourceExts, 'svg'], sourceExts: [...sourceExts, 'svg'],
blockList: exclusionList([/^(?!.*node_modules).*\/dist\/.*/]), blockList: exclusionList([/^(?!.*node_modules).*\/dist\/.*/]),
unstable_enableSymlinks: true,
unstable_enablePackageExports: true,
}, },
}, };
{
module.exports = withNxMetro(mergeConfig(defaultConfig, customConfig), {
// Change this to true to see debugging info. // Change this to true to see debugging info.
// Useful if you have issues resolving modules // Useful if you have issues resolving modules
debug: false, debug: false,
// all the file extensions used for imports other than 'ts', 'tsx', 'js', 'jsx', 'json' // all the file extensions used for imports other than 'ts', 'tsx', 'js', 'jsx', 'json'
extensions: [], extensions: [],
// the project root to start the metro server
projectRoot: __dirname,
// Specify folders to watch, in addition to Nx defaults (workspace libraries and node_modules) // Specify folders to watch, in addition to Nx defaults (workspace libraries and node_modules)
watchFolders: [] watchFolders: [],
} });
);
})();

View File

@ -3,6 +3,7 @@
"version": "0.0.1", "version": "0.0.1",
"private": true, "private": true,
"dependencies": { "dependencies": {
"@react-native/metro-config": "*",
"@testing-library/jest-native": "*", "@testing-library/jest-native": "*",
"@testing-library/react-native": "*", "@testing-library/react-native": "*",
"react": "*", "react": "*",

View File

@ -25,6 +25,7 @@ function getTargets(options: NormalizedSchema) {
architect.start = { architect.start = {
executor: '@nx/react-native:start', executor: '@nx/react-native:start',
dependsOn: ['ensure-symlink', 'sync-deps', 'pod-install'],
options: { options: {
port: 8081, port: 8081,
}, },
@ -39,11 +40,13 @@ function getTargets(options: NormalizedSchema) {
architect['run-ios'] = { architect['run-ios'] = {
executor: '@nx/react-native:run-ios', executor: '@nx/react-native:run-ios',
dependsOn: ['ensure-symlink', 'sync-deps', 'pod-install'],
options: {}, options: {},
}; };
architect['bundle-ios'] = { architect['bundle-ios'] = {
executor: '@nx/react-native:bundle', executor: '@nx/react-native:bundle',
dependsOn: ['ensure-symlink'],
outputs: ['{options.bundleOutput}'], outputs: ['{options.bundleOutput}'],
options: { options: {
entryFile: options.entryFile, entryFile: options.entryFile,
@ -54,6 +57,7 @@ function getTargets(options: NormalizedSchema) {
architect['run-android'] = { architect['run-android'] = {
executor: '@nx/react-native:run-android', executor: '@nx/react-native:run-android',
dependsOn: ['ensure-symlink', 'sync-deps'],
options: {}, options: {},
}; };
@ -63,12 +67,14 @@ function getTargets(options: NormalizedSchema) {
`{projectRoot}/android/app/build/outputs/bundle`, `{projectRoot}/android/app/build/outputs/bundle`,
`{projectRoot}/android/app/build/outputs/apk`, `{projectRoot}/android/app/build/outputs/apk`,
], ],
dependsOn: ['ensure-symlink', 'sync-deps'],
options: {}, options: {},
}; };
architect['build-ios'] = { architect['build-ios'] = {
executor: '@nx/react-native:build-ios', executor: '@nx/react-native:build-ios',
outputs: ['{projectRoot}/ios/build/Build'], outputs: ['{projectRoot}/ios/build/Build'],
dependsOn: ['ensure-symlink', 'sync-deps', 'pod-install'],
options: {}, options: {},
}; };
@ -79,6 +85,7 @@ function getTargets(options: NormalizedSchema) {
architect['bundle-android'] = { architect['bundle-android'] = {
executor: '@nx/react-native:bundle', executor: '@nx/react-native:bundle',
dependsOn: ['ensure-symlink'],
outputs: ['{options.bundleOutput}'], outputs: ['{options.bundleOutput}'],
options: { options: {
entryFile: options.entryFile, entryFile: options.entryFile,

View File

@ -24,6 +24,7 @@ import {
reactNativeCommunityCli, reactNativeCommunityCli,
reactNativeCommunityCliAndroid, reactNativeCommunityCliAndroid,
reactNativeCommunityCliIos, reactNativeCommunityCliIos,
reactNativeMetroConfigVersion,
reactNativeSvgTransformerVersion, reactNativeSvgTransformerVersion,
reactNativeSvgVersion, reactNativeSvgVersion,
reactNativeVersion, reactNativeVersion,
@ -90,6 +91,7 @@ export function updateDependencies(host: Tree) {
'@types/node': typesNodeVersion, '@types/node': typesNodeVersion,
'@types/react': typesReactVersion, '@types/react': typesReactVersion,
'@types/react-native': typesReactNativeVersion, '@types/react-native': typesReactNativeVersion,
'@react-native/metro-config': reactNativeMetroConfigVersion,
'@react-native-community/cli': reactNativeCommunityCli, '@react-native-community/cli': reactNativeCommunityCli,
'@react-native-community/cli-platform-android': '@react-native-community/cli-platform-android':
reactNativeCommunityCliAndroid, reactNativeCommunityCliAndroid,
@ -98,6 +100,7 @@ export function updateDependencies(host: Tree) {
'@testing-library/jest-native': testingLibraryJestNativeVersion, '@testing-library/jest-native': testingLibraryJestNativeVersion,
'jest-react-native': jestReactNativeVersion, 'jest-react-native': jestReactNativeVersion,
metro: metroVersion, metro: metroVersion,
'metro-config': metroVersion,
'metro-resolver': metroVersion, 'metro-resolver': metroVersion,
'metro-babel-register': metroVersion, 'metro-babel-register': metroVersion,
'metro-react-native-babel-preset': metroVersion, 'metro-react-native-babel-preset': metroVersion,
@ -108,7 +111,6 @@ export function updateDependencies(host: Tree) {
'@babel/preset-react': babelPresetReactVersion, '@babel/preset-react': babelPresetReactVersion,
...(isPnpm ...(isPnpm
? { ? {
'metro-config': metroVersion, // metro-config is used by metro.config.js
'@babel/runtime': babelRuntimeVersion, // @babel/runtime is used by react-native-svg '@babel/runtime': babelRuntimeVersion, // @babel/runtime is used by react-native-svg
} }
: {}), : {}),

View File

@ -170,7 +170,6 @@ function createFiles(host: Tree, options: NormalizedSchema) {
{ {
...options, ...options,
...names(options.name), ...names(options.name),
tmpl: '',
offsetFromRoot: offsetFromRoot(options.projectRoot), offsetFromRoot: offsetFromRoot(options.projectRoot),
rootTsConfigPath: getRelativePathToRootTsConfig( rootTsConfigPath: getRelativePathToRootTsConfig(
host, host,

View File

@ -0,0 +1,39 @@
import { Tree, getProjects, updateProjectConfiguration } from '@nx/devkit';
/**
* This migration adds dependsOn to project.json.
*
*/
export default async function update(tree: Tree) {
const projects = getProjects(tree);
for (const [name, config] of projects.entries()) {
if (config.targets?.['start']?.executor === '@nx/react-native:start') {
config.targets['start'].dependsOn = [
'ensure-symlink',
'sync-deps',
'pod-install',
];
config.targets['run-ios'].dependsOn = [
'ensure-symlink',
'sync-deps',
'pod-install',
];
config.targets['bundle-ios'].dependsOn = ['ensure-symlink'];
config.targets['run-android'].dependsOn = ['ensure-symlink', 'sync-deps'];
config.targets['build-android'].dependsOn = [
'ensure-symlink',
'sync-deps',
];
config.targets['build-ios'].dependsOn = [
'ensure-symlink',
'sync-deps',
'pod-install',
];
config.targets['pod-install'].dependsOn = ['ensure-symlink', 'sync-deps'];
config.targets['bundle-android'].dependsOn = ['ensure-symlink'];
updateProjectConfiguration(tree, name, config);
}
}
}

View File

@ -0,0 +1,71 @@
import { Tree, getProjects } from '@nx/devkit';
import { join } from 'path';
/**
* This migration updates metro.config.js to export config as a default.
*
*/
export default async function update(tree: Tree) {
const projects = getProjects(tree);
for (const [_, config] of projects.entries()) {
if (config.targets?.['start']?.executor === '@nx/react-native:start') {
if (tree.exists(join(config.root, 'metro.config.js'))) {
const oldConfig = tree
.read(join(config.root, 'metro.config.js'))
.toString();
tree.write(
join(config.root, 'metro-v71.config.js'),
oldConfigComment + oldConfig
);
tree.write(join(config.root, 'metro.config.js'), content);
}
}
}
}
const oldConfigComment = `/**
* Old custom configuration for React Native v0.71.
* From @react-native/metro-config 0.72.1, it is no longer necessary to use a config function to access the complete default config.
* Please port your custom configuration to metro.config.js.
* Please see https://reactnative.dev/docs/metro to learn about configuration.
*/
`;
const content = `
const { withNxMetro } = require('@nx/react-native');
const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config');
const exclusionList = require('metro-config/src/defaults/exclusionList');
const defaultConfig = getDefaultConfig(__dirname);
const { assetExts, sourceExts } = defaultConfig.resolver;
/**
* Metro configuration
* https://facebook.github.io/metro/docs/configuration
*
* @type {import('metro-config').MetroConfig}
*/
const customConfig = {
transformer: {
babelTransformerPath: require.resolve('react-native-svg-transformer'),
},
resolver: {
assetExts: assetExts.filter((ext) => ext !== 'svg'),
sourceExts: [...sourceExts, 'svg'],
blockList: exclusionList([/^(?!.*node_modules).*\/dist\/.*/]),
unstable_enableSymlinks: true,
unstable_enablePackageExports: true,
},
};
module.exports = withNxMetro(mergeConfig(defaultConfig, customConfig), {
// Change this to true to see debugging info.
// Useful if you have issues resolving modules
debug: false,
// all the file extensions used for imports other than 'ts', 'tsx', 'js', 'jsx', 'json'
extensions: [],
// Specify folders to watch, in addition to Nx defaults (workspace libraries and node_modules)
watchFolders: [],
});
`;

View File

@ -18,7 +18,9 @@ export function getCliOptions<T>(
const cliKey = optionKeysInCamelName.includes(optionKey) const cliKey = optionKeysInCamelName.includes(optionKey)
? names(optionKey).propertyName ? names(optionKey).propertyName
: names(optionKey).fileName; // cli uses kebab case as default : names(optionKey).fileName; // cli uses kebab case as default
if (typeof optionValue === 'boolean' && optionValue) { if (Array.isArray(optionValue)) {
acc.push(`--${cliKey}`, optionValue.join(','));
} else if (typeof optionValue === 'boolean' && optionValue) {
// no need to pass in the value when it is true, just the flag name // no need to pass in the value when it is true, just the flag name
acc.push(`--${cliKey}`); acc.push(`--${cliKey}`);
} else { } else {

View File

@ -24,7 +24,15 @@ ${chalk.bold('sudo xcode-select --switch /Applications/Xcode.app')}
export function runPodInstall( export function runPodInstall(
iosDirectory: string, iosDirectory: string,
install: boolean = true, install: boolean = true,
buildFolder?: string options: {
buildFolder?: string;
repoUpdate?: boolean;
deployment?: boolean;
} = {
buildFolder: './build',
repoUpdate: false,
deployment: false,
}
): GeneratorCallback { ): GeneratorCallback {
return () => { return () => {
if (platform() !== 'darwin') { if (platform() !== 'darwin') {
@ -39,16 +47,32 @@ export function runPodInstall(
logger.info(`Running \`pod install\` from "${iosDirectory}"`); logger.info(`Running \`pod install\` from "${iosDirectory}"`);
return podInstall(iosDirectory, buildFolder); return podInstall(iosDirectory, options);
}; };
} }
export function podInstall(iosDirectory: string, buildFolder?: string) { export function podInstall(
iosDirectory: string,
options: {
buildFolder?: string;
repoUpdate?: boolean;
deployment?: boolean;
} = {
buildFolder: './build',
repoUpdate: false,
deployment: false,
}
) {
try { try {
execSync('pod install', { execSync(
`pod install ${options.repoUpdate ? '--repo-update' : ''} ${
options.deployment ? '--deployment' : ''
}`,
{
cwd: iosDirectory, cwd: iosDirectory,
stdio: 'inherit', stdio: 'inherit',
}); }
);
} catch (e) { } catch (e) {
logger.error(podInstallErrorMessage); logger.error(podInstallErrorMessage);
throw e; throw e;

View File

@ -1,15 +1,16 @@
export const nxVersion = require('../../package.json').version; export const nxVersion = require('../../package.json').version;
export const reactNativeVersion = '0.71.10'; export const reactNativeVersion = '0.72.3';
export const typesReactNativeVersion = '0.71.7'; export const typesReactNativeVersion = '0.72.2';
export const typesNodeVersion = '18.14.4'; export const typesNodeVersion = '18.14.4';
export const metroVersion = '0.74.1'; export const metroVersion = '0.76.7';
export const reactNativeMetroConfigVersion = '^0.72.9';
export const reactNativeCommunityCli = '10.2.2'; export const reactNativeCommunityCli = '11.3.5';
export const reactNativeCommunityCliIos = '10.2.1'; export const reactNativeCommunityCliIos = '11.3.5';
export const reactNativeCommunityCliAndroid = '10.2.0'; export const reactNativeCommunityCliAndroid = '11.3.5';
export const reactVersion = '18.2.0'; export const reactVersion = '18.2.0';
export const reactDomVersion = '18.2.0'; export const reactDomVersion = '18.2.0';
@ -24,7 +25,7 @@ export const jestReactNativeVersion = '18.0.0';
export const reactNativeSvgTransformerVersion = '1.0.0'; export const reactNativeSvgTransformerVersion = '1.0.0';
export const reactNativeSvgVersion = '13.9.0'; export const reactNativeSvgVersion = '13.9.0';
export const babelRuntimeVersion = '7.21.0'; export const babelRuntimeVersion = '7.22.6';
export const reactNativeAsyncStorageVersion = '1.18.2'; export const reactNativeAsyncStorageVersion = '1.19.0';
export const reactNativeSafeAreaContextVersion = '4.5.3'; export const reactNativeSafeAreaContextVersion = '4.7.1';

705
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff